diff --git a/README.md b/README.md index 12f2bfd..ef276d5 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,39 @@ # BroSDK +`@brotocol-xyz/bro-sdk` is a TypeScript SDK designed to integrate with Brotocol's on-chain and off-chain infrastructure. It is web3 library-agnostic, meaning you can use your preferred library to send and broadcast transactions while the SDK handles the rest. + 🐙 **Brotocol isn't just a bridge—it's the liquidity layer for Bitcoin and the essential connector for Bitcoin DeFi** 🐙 -BroSDK enables seamless asset transfers between Bitcoin, Stacks, and EVM-compatible blockchains. It supports cross-chain swaps, Runes & BRC20 metaprotocols, and DEX aggregator integrations. +## Table of Contents -The SDK allows users to interact with Brotocol smart contracts from backend environments, browsers, and mobile apps. It securely handles cross-chain transfers, fee estimation, route planning, and transaction size calculations by using Brotocol's on-chain and off-chain infrastructure. +- [BroSDK](#brosdk) + - [Table of Contents](#table-of-contents) + - [Features](#features) + - [Installation](#installation) + - [Usage](#usage) + - [Supported Chains](#supported-chains) + - [Mainnet Chains](#mainnet-chains) + - [Testnet Chains](#testnet-chains) + - [Helpers](#helpers) + - [Supported Tokens](#supported-tokens) + - [Retrieve a `TokenId`](#retrieve-a-tokenid) + - [Available Routes](#available-routes) + - [Basic Operations](#basic-operations) + - [`bridgeInfoFrom` methods](#bridgeinfofrom-methods) + - [`estimateBridgeTransactionFrom` methods](#estimatebridgetransactionfrom-methods) + - [`bridgeFrom` methods](#bridgefrom-methods) + - [License](#license) + +## Features + +- Asset transfers between Bitcoin, Stacks, and EVM-compatible blockchains +- Support for Runes and BRC20 metaprotocols +- Cross-chain swaps and DEX aggregator integrations +- Flexible design, allowing integration with any Bitcoin and web3 library ## Installation -### Prerequisites - -- [Node.js](https://nodejs.org/en) -- [pnpm](https://pnpm.io/) - -### Install +With [pnpm](https://pnpm.io/): ```bash pnpm install @brotocol-xyz/bro-sdk @@ -21,261 +41,203 @@ pnpm install @brotocol-xyz/bro-sdk ## Usage -The [`BroSDK`](./src/BroSDK.ts) class provides the core functions of the library. To create an instance: +The [`BroSDK`](https://releases-latest.xlink-sdk.pages.dev/classes/index.BroSDK) class provides the core functions of the library. To create an instance: -```typescript +```ts import { BroSDK } from "@brotocol-xyz/bro-sdk" const sdk = new BroSDK() ``` For the full API reference, including a full list of available methods and their usage, visit the [SDK Documentation](https://releases-latest.xlink-sdk.pages.dev). -### Supported Blockchains and Tokens +### Supported Chains -#### [`KnownChainId`](https://releases-latest.xlink-sdk.pages.dev/modules/index.KnownChainId) +#### Mainnet Chains -Defines types and utility functions for supported networks, ensuring only valid chain IDs are used within the SDK. +- **Bitcoin**, **Runes** & **BRC20** +- **Stacks** +- **EVM**: Ethereum, BSC, CoreDAO, Bsquared, BOB, Bitlayer, Lorenzo, Merlin, AILayer, Mode, XLayer, Arbitrum, Aurora, Manta, Linea, Base + +#### Testnet Chains + +- **Bitcoin**, **Runes** & **BRC20** +- **Stacks** +- **EVM**: Sepolia, BSC Testnet, CoreDAO Testnet, Blife Testnet, Bitboy Testnet, Bera Testnet + +#### Helpers + +The [`KnownChainId`](https://releases-latest.xlink-sdk.pages.dev/modules/index.KnownChainId) namespace defines types and utility functions for all supported mainnet and testnet networks. + +Usage example: + +```ts +import { KnownChainId } from "@brotocol-xyz/bro-sdk" + +// Bitcoin +const bitcoinChainId = KnownChainId.Bitcoin.Mainnet +const bitcoinTestnetChainId = KnownChainId.Bitcoin.Testnet + +// EVM +const ethereumChainId = KnownChainId.EVM.Ethereum +const ethereumTestnetChainId = KnownChainId.EVM.Sepolia + +// Utility function usage example +KnownChainId.isEVMTestnetChain(KnownChainId.EVM.Sepolia) // Returns true +KnownChainId.isEVMMainnetChain(KnownChainId.EVM.Sepolia) // Returns false +``` + +> [!NOTE] +> Runes and BRC20 metaprotocols are treated as distinct chains within the SDK, even though they share Bitcoin as the underlying blockchain. + + -#### [`KnownTokenId`](https://releases-latest.xlink-sdk.pages.dev/modules/index.KnownTokenId) +### Supported Tokens -Defines types, utility functions, and supported tokens within the SDK. +Token support is **dynamic**, meaning new tokens can be added without requiring SDK updates. Instead of relying on a static list, the SDK provides methods to fetch supported tokens at runtime. Tokens are represented using the `TokenId` type — this is how the library internally handles and identifies tokens. -| Namespace | Tokens | -| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -| Bitcoin | `BTC` | -| Runes | _To be confirmed_ | -| BRC20 | _To be confirmed_ | -| Stacks | `sUSDT`, `sLUNR`, `aBTC`, `ALEX`, `sSKO`, `vLiSTX`, `vLiALEX`, `vLiaBTC`,`uBTC`, `DB20`, `DOG`, `STX`, `TRUMP` | -| EVM | `USDT`, `sUSDT`, `USDC`, `aBTC`, `WTCB`, `BTCB`, `cbBTC`, `uBTC`, `wuBTC`, `STX`, `vLiSTX`, `ALEX`, `vLiALEX`, `LUNR`, `SKO`, `DB20`, `DOG`, `TRUMP` | +Also, check the [`KnownTokenId`](https://releases-latest.xlink-sdk.pages.dev/modules/index.KnownTokenId) namespace to see types and utility functions for all supported tokens. -### Use Cases +#### Retrieve a `TokenId` -Create an instance of the SDK with default options: +```ts +// For BRC20 provide the tick symbol +const brc20Token = await sdk.brc20TickToBRC20Token( + KnownChainId.BRC20.Mainnet, + "alex$", +) -```typescript -import { BroSDK } from "@brotocol-xyz/bro-sdk" -const broSdk = new BroSDK() +// For Runes provide the runes ID +const runesToken = await sdk.runesIdToRunesToken( + KnownChainId.Runes.Mainnet, + "500:20", +) + +// For Stacks provide the contract address +const stacksToken = await sdk.stacksAddressToStacksToken( + KnownChainId.Stacks.Mainnet, + { + deployerAddress: "SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK", + contractName: "token-abtc", + }, +) + +// For EVM tokens provide the contract address +const evmToken = await sdk.evmAddressToEVMToken( + KnownChainId.EVM.Ethereum, + "0x31761a152F1e96F966C041291644129144233b0B", +) ``` -#### Bridge from Stacks +If a token is **unsupported**, these functions return `Promise`. -Use case showcasing a transfer of 100 `sUSDT` from Stacks to `USDT` on Ethereum using BroSDK. +> [!NOTE] +> Some Stacks and EVM tokens are still statically defined in `KnownTokenId.Stacks` and `KnownTokenId.EVM` for backward compatibility, but future additions will also be dynamically handled. -```typescript -import { - BridgeFromStacksInput, - KnownChainId, - KnownTokenId, - toSDKNumberOrUndefined, -} from '@brotocol-xyz/bro-sdk'; -import { serializeCVBytes, makeContractCall, broadcastTransaction } from '@stacks/transactions'; +> [!WARNING] +> +> `TokenId` values **might change in future updates** (no backward compatibility guaranteed). To ensure validity, always get fresh `TokenId`s at runtime using SDK methods—never cache them or construct them manually. -// Retrieve bridge information -const bridgeInfo = await broSdk.bridgeInfoFromStacks({ +### Available Routes + +```ts +// Get all Brotocol available routes +const allRoutes = await sdk.getPossibleRoutes() + +// Get routes filtered by source chain +const routesBySourceChain = await sdk.getPossibleRoutes({ + fromChain: KnownChainId.BRC20.Mainnet, +}) + +// Get routes filtered by source and target chain +const routesBySourceAndTargetChain = await sdk.getPossibleRoutes({ + fromChain: KnownChainId.BRC20.Mainnet, + toChain: KnownChainId.EVM.Ethereum, +}) + +// Check if a specific token pair is supported for at least one route +const isSupported = await sdk.isSupportedRoute({ + fromChain: KnownChainId.BRC20.Mainnet, + toChain: KnownChainId.EVM.Ethereum, + fromToken: brc20Token as KnownTokenId.BRC20Token, + toToken: evmToken as KnownTokenId.EVMToken, +}) + +// If the token pair is supported, get available routes for that pair +if (isSupported) { + const routesByPair = await sdk.getPossibleRoutes({ + fromChain: KnownChainId.BRC20.Mainnet, + toChain: KnownChainId.EVM.Ethereum, + fromToken: brc20Token as KnownTokenId.BRC20Token, + toToken: evmToken as KnownTokenId.EVMToken, + }) +} +``` + +### Basic Operations + +The SDK provides three main methods for handling cross-chain asset transfers. + +- [`bridgeInfoFrom`](#bridgeinfofrom-methods) +- [`estimateBridgeTransactionFrom`](#estimatebridgetransactionfrom-methods) +- [`bridgeFrom`](#bridgefrom-methods) + +#### `bridgeInfoFrom` methods + +Retrieve data to perform a cross-chain transfer: + +- Validate whether the route is supported (throws an error if not). +- Retrieve Brotocol fee values and the exact amount that will arrive on destination chain. + +> [!NOTE] +> These methods do not check the bridge's min/max amount limits. These checks are enforced on-chain, and the transaction will revert if the amount conditions are not met. + +```ts +import { toSDKNumberOrUndefined } from "@brotocol-xyz/bro-sdk" + +// Retrieve bridge info to perform a transfer from Stacks to EVM +const bridgeInfo = await sdk.bridgeInfoFromStacks({ fromChain: KnownChainId.Stacks.Mainnet, toChain: KnownChainId.EVM.Ethereum, - fromToken: KnownTokenId.Stacks.sUSDT, - toToken: KnownTokenId.EVM.USDT, - amount: toSDKNumberOrUndefined(100), -}); - -console.log("Bridge Info:", bridgeInfo); - -// Define bridge operation input -const bridgeFromStacksInput: BridgeFromStacksInput = { - fromChain: KnownChainId.Stacks.Mainnet, - toChain: KnownChainId.EVM.Ethereum, - fromToken: KnownTokenId.Stacks.sUSDT, - toToken: KnownTokenId.EVM.USDT, - fromAddress: /* Sender Stacks principal */, - toAddress: /* Receiver EVM address */, - amount: toSDKNumberOrUndefined(100), - sendTransaction: async (tx: ContractCallOptions) => { - /** - * Implementation for sending transaction on Stacks mainnet. - * Refer to: https://github.com/hirosystems/stacks.js/tree/main/packages/transactions#smart-contract-function-call - */ - const transaction = await makeContractCall({ - contractAddress: tx.contractAddress, - contractName: tx.contractName, - functionName: tx.functionName, - functionArgs: tx.functionArgs, - postConditions: /* Add post conditions if necessary */, - validateWithAbi: true, - senderKey: /* Sender private key */, - network: "mainnet", - }); - - const broadcastResponse = await broadcastTransaction(transaction, "mainnet"); - return { txid: broadcastResponse.txid }; - }, -}; - -// Example of how a `sendTransaction` argument would look like -const contractCallOptionsExample: ContractCallOptions = { - contractAddress: "SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK", - contractName: "cross-peg-out-endpoint-v2-01", - functionName: "transfer-to-unwrap", - functionArgs: [].map(arg => serializeCVBytes(arg)), // Array elements must be Clarity values -}; - -// Perform the bridge operation -const result = await broSdk.bridgeFromStacks(bridgeFromStacksInput); -console.log("Transaction ID:", result.txid); + fromToken: stacksToken as KnownTokenId.StacksToken, + toToken: evmToken as KnownTokenId.EVMToken, + amount: toSDKNumberOrUndefined(100_000_000), // Assume 6 decimals +}) ``` -#### Bridge from EVM +#### `estimateBridgeTransactionFrom` methods -Use case showcasing a transfer of 100 `USDT` from Ethereum to `UsSDT` on Stacks using BroSDK. +Estimate the transaction fee and virtual size (vbytes) for bridging from Bitcoin-based networks (Bitcoin, Runes, BRC20). Fees are calculated as: -```typescript -import { - BridgeFromEVMInput, - KnownChainId, - KnownTokenId, - toSDKNumberOrUndefined, -} from "@brotocol-xyz/bro-sdk" -import { ethers } from "ethers"; - -// Retrieve bridge information -const bridgeInfo = await broSdk.bridgeInfoFromEVM({ - fromChain: KnownChainId.EVM.Ethereum, - toChain: KnownChainId.Stacks.Mainnet, - fromToken: KnownTokenId.EVM.USDT, - toToken: KnownTokenId.Stacks.sUSDT, - amount: toSDKNumberOrUndefined(100), -}); - -console.log("Bridge Info:", bridgeInfo); - -// Example signer setup using ethers.js -const provider = new ethers.providers.JsonRpcProvider("https://mainnet.someprovider.io/YOUR_PROJECT_ID"); -const signer = new ethers.Wallet("SENDER_PRIVATE_KEY", provider); - -const bridgeFromEVMInput: BridgeFromEVMInput = { - fromChain: KnownChainId.EVM.Ethereum, - toChain: KnownChainId.Stacks.Mainnet, - fromToken: KnownTokenId.EVM.USDT, - toToken: KnownTokenId.Stacks.sUSDT, - fromAddress: /* Sender EVM address */, - toAddress: /* Receiver Stacks principal */, - amount: toSDKNumberOrUndefined(100), - sendTransaction: async (tx: - { - from: EVMAddress /* Sender EVM address */ - to: EVMAddress /* Bridge Endpoint address */ - data: Uint8Array /* Transaction data */ - recommendedGasLimit: SDKNumber /* Recommended gas limit */ - value?: SDKNumber /* Transaction value */ - } - ): Promise<{ txHash: string }> => { - /** - * Implementation for sending transaction on Ethereum mainnet - * See https://docs.ethers.org/v5/api/contract/contract/ for reference - */ - const txRequest = { - from: tx.from, - to: tx.to, - data: ethers.utils.hexlify(tx.data), - gasLimit: ethers.BigNumber.from(tx.recommendedGasLimit.split(" ")[0]), // Convert string to BigNumber - }; - - const sentTx = await signer.sendTransaction(txRequest); - const receipt = await sentTx.wait(); - return { txHash: receipt.transactionHash }; - }, -}; - -// Perform the bridge operation -const result = await broSdk.bridgeFromEVM(bridgeFromEVMInput); -console.log("Transaction ID:", result.txHash); +```any +fee = virtualSize [vbytes] × networkFeeRate [sat/vbyte] ``` -#### Bridge from Bitcoin +`networkFeeRate` is provided by dev. Typical fee rates range from 1–100 sat/vbyte, depending on network congestion and desired confirmation speed. See this [reference](https://learnmeabitcoin.com/technical/transaction/size/) for more on transaction size. -Use case showcasing a transfer of 1 `BTC` from Bitcoin to `WBTC` on Ethereum using BroSDK. +**Why is this important?** Miners prioritize transactions with higher fees per vbyte. Accurately estimating the transaction virtual size allows to set an appropriate fee, so the transaction is confirmed in a timely manner. -```typescript -import { - BridgeFromBitcoinInput, - KnownChainId, - KnownTokenId, - toSDKNumberOrUndefined, -} from "@brotocol-xyz/bro-sdk" -/* Use your preferred Bitcoin libs here */ -import { Psbt, networks, Transaction, script } from "bitcoinjs-lib"; -import { ECPairFactory } from "ecpair"; -import * as tinysecp from "tiny-secp256k1"; -import axios from "axios"; +See the [`examples/bridgeFrom/Bitcoin.ts`](https://github.com/Brotocol-xyz/bro-sdk/tree/master/examples/bridgeFrom/Bitcoin.ts) file for usage example. -// Retrieve bridge information -const bridgeInfo = await broSdk.bridgeInfoFromBitcoin({ - fromChain: KnownChainId.Bitcoin.Mainnet, - toChain: KnownChainId.EVM.Ethereum, - fromToken: KnownTokenId.Bitcoin.BTC, - toToken: KnownTokenId.EVM.WBTC, - amount: toSDKNumberOrUndefined(1), -}); +#### `bridgeFrom` methods -console.log("Bridge Info:", bridgeInfo) +Once the route is validated, the cross-chain transfer can be initiated. These methods **construct and submit** the transaction on the source chain. -const bridgeFromBitcoinInput: BridgeFromBitcoinInput = { - fromChain: KnownChainId.Bitcoin.Mainnet, - fromToken: KnownTokenId.Bitcoin.BTC, - toChain: KnownChainId.EVM.Ethereum, - toToken: KnownTokenId.EVM.WBTC, - fromAddress: /* Sender Bitcoin address */, - toAddress: /* Receiver EVM address */, - amount: toSDKNumberOrUndefined(1), - networkFeeRate: 10n, - reselectSpendableUTXOs: async ( - satsToSend: bigint, - pinnedUTXOs: UTXOSpendable[], - lastTimeSelectedUTXOs: UTXOSpendable[] - ): Promise => { - /** - * Implementation for selecting spendable UTXOs from the wallet - * This should fetch available UTXOs from a Bitcoin node or explorer API - */ - return []; - }, - signPsbt: async (tx: { psbt: Uint8Array; signInputs: number[] }): Promise<{ psbt: Uint8Array }> => { - /** - * Implementation for signing a Bitcoin PSBT (Partially Signed Bitcoin Transaction) - * See https://github.com/bitcoinjs/bitcoinjs-lib for reference - */ - let psbt = Psbt.fromBuffer(tx.psbt); - tx.signInputs.forEach((index) => { - psbt.signInput(index, keyPair); - }); - psbt.finalizeAllInputs(); - return { psbt: psbt.toBuffer() }; - }, - sendTransaction: async (tx: { hex: string }): Promise<{ txid: string }> => { - /** - * Implementation for broadcasting a Bitcoin transaction with Axios - * Using a Bitcoin node or explorer API (e.g., Blockstream API) - */ - const response = await axios.post("https://some-mempool/api/tx", tx.hex, { - headers: { "Content-Type": "text/plain" }, - }); - return { txid: response.data }; - }, -}; +> [!IMPORTANT] +> The SDK does **not always** broadcast transactions—it provides the data required to sign and send them. The `sendTransaction` function parameter must be implemented by the developer using their preferred web3 library. The SDK provides the necessary arguments, including contract addresses, function to call and call data. -// Perform the bridge operation -const result = await broSdk.bridgeFromBitcoin(bridgeFromBitcoinInput); -console.log("Transaction ID:", result.txid); -``` +Examples on how to use the `bridgeFrom` methods can be found in the [examples folder](https://github.com/Brotocol-xyz/bro-sdk/tree/master/examples/bridgeFrom/). ## License -This project is licensed under the terms of the [MIT license](LICENSE). \ No newline at end of file +This project is licensed under the terms of the [MIT license](LICENSE). diff --git a/docs/dev-instructions.md b/docs/dev-instructions.md index 3276680..5660216 100644 --- a/docs/dev-instructions.md +++ b/docs/dev-instructions.md @@ -1,4 +1,4 @@ -# Test Instructions +# Dev Instructions ## Visualize Typedoc Documentation @@ -10,9 +10,19 @@ pnpm run docs:watch In a new terminal window, run: +```bash +pnpx http-server generated/docs +``` + +or + ```bash cd generated/docs python -m http.server 8080 ``` Open `http://localhost:8080` in your browser to view the documentation. + +## README Examples + +Version history of the README examples is tracked at [`../examples/bridgeFrom`](../examples/bridgeFrom). diff --git a/examples/bridgeFrom/BRC20.ts b/examples/bridgeFrom/BRC20.ts new file mode 100644 index 0000000..250f689 --- /dev/null +++ b/examples/bridgeFrom/BRC20.ts @@ -0,0 +1,161 @@ +// Bridge From BRC20 + +import { + GetConfirmedSpendableUTXOFn, + reselectSpendableUTXOsFactory, + UTXOBasic, +} from "../../../src/bitcoinHelpers" +import { + BroSDK, + KnownTokenId, + KnownChainId, + BridgeFromBRC20Input, +} from "../../../src/index" + +import { Psbt, payments, networks } from "bitcoinjs-lib" +import axios from "axios" + +const sdk = new BroSDK() + +// For BRC20 provide the tick symbol +const brc20Token: KnownTokenId.BRC20Token = (await sdk.brc20TickToBRC20Token( + KnownChainId.BRC20.Mainnet, + "alex$", +))! + +// For EVM tokens provide the contract address +const evmToken: KnownTokenId.EVMToken = (await sdk.evmAddressToEVMToken( + KnownChainId.EVM.Ethereum, + "0x31761a152F1e96F966C041291644129144233b0B", +))! + +// Mock functions for key management +const getPublicKey = async (): Promise => { + // This is a mock implementation + // In a real implementation, this would return the user's public key + return "02a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7" +} + +const signTx = async (hex: string): Promise => { + // This is a mock implementation + // In a real implementation, this would sign the transaction with the user's private key + return "mock_signature" +} + +const publicKey = await getPublicKey() +const { address: senderAddress, output: scriptPubKey } = payments.p2wpkh({ + pubkey: Buffer.from(publicKey, "hex"), + network: networks.bitcoin, +}) + +// Select UTXOs to spend +const reselectSpendableNetworkFeeUTXOs: BridgeFromBRC20Input["reselectSpendableNetworkFeeUTXOs"] = + async (satsToSend, lastTimeSelectedUTXOs) => { + // Example of available UTXOs from a Bitcoin node or API + const availableUTXOs: UTXOBasic[] = [ + { + txId: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + index: 0, + amount: 5000n, + }, + { + txId: "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", + index: 1, + amount: 3000n, + }, + { + txId: "7890abcdef1234567890abcdef1234567890abcdef1234567890abcdef123456", + index: 2, + amount: 2000n, + }, + ] + + const getUTXOSpendable: GetConfirmedSpendableUTXOFn = async ( + utxo: UTXOBasic, + ) => { + // For this example, we'll create a simple UTXOSpendable object + return { + ...utxo, + scriptPubKey: scriptPubKey!, + addressType: "p2wpkh", + blockHeight: 800000n, // Example block height + } + } + + // Create the reselect function with factory helper + const reselectFn = reselectSpendableUTXOsFactory( + availableUTXOs, + getUTXOSpendable, + ) + + return reselectFn(satsToSend, lastTimeSelectedUTXOs) + } + +const sendTransaction: BridgeFromBRC20Input["sendTransaction"] = async tx => { + /** + * Implementation example for broadcasting a Bitcoin transaction with Axios + */ + const response = await axios.post("https://blockstream.info/api/tx", tx.hex, { + headers: { "Content-Type": "text/plain" }, + }) + return { txid: response.data } +} + +const bridgeFromBRC20Input: BridgeFromBRC20Input = { + fromChain: KnownChainId.BRC20.Mainnet, + fromToken: brc20Token, + toChain: KnownChainId.EVM.Ethereum, + toToken: evmToken, + fromAddress: senderAddress!, + fromAddressScriptPubKey: scriptPubKey!, + toAddress: "0x31751a152F1e95F966C041291644129144233b0B", + inputInscriptionUTXO: { + txId: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + index: 0, + amount: 1000n, + scriptPubKey: scriptPubKey!, + addressType: "p2wpkh", + }, + networkFeeRate: 10n, + reselectSpendableNetworkFeeUTXOs, + networkFeeChangeAddress: senderAddress!, + networkFeeChangeAddressScriptPubKey: scriptPubKey!, + signPsbt: async tx => { + const psbt = Psbt.fromBuffer(Buffer.from(tx.psbt)) + + for (const index of tx.signBitcoinInputs) { + const signature = await signTx(psbt.toHex()) + + // Add the signature to the PSBT + psbt.updateInput(index, { + partialSig: [ + { + pubkey: Buffer.from(await getPublicKey(), "hex"), + signature: Buffer.from(signature, "hex"), + }, + ], + }) + } + + for (const index of tx.signInscriptionInputs) { + const signature = await signTx(psbt.toHex()) + + // Add the signature to the PSBT + psbt.updateInput(index, { + partialSig: [ + { + pubkey: Buffer.from(await getPublicKey(), "hex"), + signature: Buffer.from(signature, "hex"), + }, + ], + }) + } + + psbt.finalizeAllInputs() + return { psbt: psbt.toBuffer() } + }, + sendTransaction, +} + +const result = await sdk.bridgeFromBRC20(bridgeFromBRC20Input) +console.log("Bitcoin Transaction ID:", result.txid) diff --git a/examples/bridgeFrom/Bitcoin.ts b/examples/bridgeFrom/Bitcoin.ts new file mode 100644 index 0000000..1053172 --- /dev/null +++ b/examples/bridgeFrom/Bitcoin.ts @@ -0,0 +1,167 @@ +// Bridge From BTC + +import { + GetConfirmedSpendableUTXOFn, + reselectSpendableUTXOsFactory, + UTXOBasic, +} from "@brotocol-xyz/bro-sdk/bitcoinHelpers" +import { + BroSDK, + KnownTokenId, + KnownChainId, + toSDKNumberOrUndefined, + BridgeFromBitcoinInput, +} from "@brotocol-xyz/bro-sdk" +import { Psbt, payments, networks } from "bitcoinjs-lib" +import axios from "axios" + +const sdk = new BroSDK() + +// For EVM tokens provide the contract address +const evmToken = await sdk.evmAddressToEVMToken( + KnownChainId.EVM.Ethereum, + "0x31761a152F1e96F966C041291644129144233b0B", +) + +// Mock functions for key management +// In a real implementation, these would be provided by the user's environment +const getPublicKey = async (): Promise => { + // This is a mock implementation + // In a real implementation, this would return the user's public key + return "02a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7" +} + +const signTx = async (hex: string): Promise => { + // This is a mock implementation + // In a real implementation, this would sign the transaction with the user's private key + return "mock_signature" +} + +// Get address and scriptPubKey +const publicKey = await getPublicKey() +const { address: senderAddress, output: scriptPubKey } = payments.p2wpkh({ + pubkey: Buffer.from(publicKey, "hex"), + network: networks.bitcoin, +}) + +// Select UTXOs to spend +const reselectSpendableUTXOs: BridgeFromBitcoinInput["reselectSpendableUTXOs"] = + async (satsToSend, lastTimeSelectedUTXOs) => { + // Example of available UTXOs from a Bitcoin node or API + const availableUTXOs: UTXOBasic[] = [ + { + txId: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + index: 0, + amount: 5000n, + }, + { + txId: "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", + index: 1, + amount: 3000n, + }, + { + txId: "7890abcdef1234567890abcdef1234567890abcdef1234567890abcdef123456", + index: 2, + amount: 2000n, + }, + ] + + // Function to convert basic UTXOs to spendable UTXOs with scriptPubKey + const getUTXOSpendable: GetConfirmedSpendableUTXOFn = async ( + utxo: UTXOBasic, + ) => { + // For this example, we'll create a simple UTXOSpendable object + return { + ...utxo, + scriptPubKey: scriptPubKey!, + addressType: "p2wpkh", + blockHeight: 800000n, // Example block height + } + } + + // Create the reselect function with factory helper + const reselectFn = reselectSpendableUTXOsFactory( + availableUTXOs, + getUTXOSpendable, + ) + + return reselectFn(satsToSend, lastTimeSelectedUTXOs) + } + +// Sign a Bitcoin PSBT +const signPsbt: BridgeFromBitcoinInput["signPsbt"] = async tx => { + /** + * Implementation example for signing a Bitcoin PSBT (Partially Signed Bitcoin Transaction) + */ + const psbt = Psbt.fromBuffer(Buffer.from(tx.psbt)) + + // For each input that needs to be signed + for (const index of tx.signInputs) { + // Get the input's sighash + const input = psbt.data.inputs[index] + // @ts-ignore + const sighash = input.sighashType + + // Sign the transaction using the mocked signTx function + const signature = await signTx(psbt.toHex()) + + // Add the signature to the PSBT + psbt.updateInput(index, { + partialSig: [ + { + pubkey: Buffer.from(await getPublicKey(), "hex"), + signature: Buffer.from(signature, "hex"), + }, + ], + }) + } + + psbt.finalizeAllInputs() + return { psbt: psbt.toBuffer() } +} + +// Broadcast the signed transaction +const sendTransaction: BridgeFromBitcoinInput["sendTransaction"] = async tx => { + /** + * Implementation example for broadcasting a Bitcoin transaction with Axios + */ + const response = await axios.post("https://blockstream.info/api/tx", tx.hex, { + headers: { "Content-Type": "text/plain" }, + }) + return { txid: response.data } +} + +// Estimate transaction fee and virtual size before performing the bridge operation +const estimateTransaction = await sdk.estimateBridgeTransactionFromBitcoin({ + fromChain: KnownChainId.Bitcoin.Mainnet, + fromToken: KnownTokenId.Bitcoin.BTC, + toChain: KnownChainId.EVM.Ethereum, + toToken: evmToken as KnownTokenId.EVMToken, + fromAddressScriptPubKey: scriptPubKey!, + fromAddress: senderAddress!, + toAddress: "0x31751a152F1e95F966C041291644129144233b0B", + amount: toSDKNumberOrUndefined(1), + networkFeeRate: 10n, + reselectSpendableUTXOs: reselectSpendableUTXOs, +}) + +console.log("Estimated Transaction: ", estimateTransaction) + +const bridgeFromBitcoinInput: BridgeFromBitcoinInput = { + fromChain: KnownChainId.Bitcoin.Mainnet, + fromToken: KnownTokenId.Bitcoin.BTC, + toChain: KnownChainId.EVM.Ethereum, + toToken: evmToken as KnownTokenId.EVMToken, + fromAddressScriptPubKey: scriptPubKey!, + fromAddress: senderAddress!, + toAddress: "0x31751a152F1e95F966C041291644129144233b0B", + amount: toSDKNumberOrUndefined(1), + networkFeeRate: 10n, // Expressed in satoshis per virtual byte (sat/vbyte). + reselectSpendableUTXOs, + signPsbt, + sendTransaction, +} + +// Perform the bridge operation +const result = await sdk.bridgeFromBitcoin(bridgeFromBitcoinInput) +console.log("Bitcoin Transaction ID:", result.txid) diff --git a/examples/bridgeFrom/EVM.ts b/examples/bridgeFrom/EVM.ts new file mode 100644 index 0000000..5b135d7 --- /dev/null +++ b/examples/bridgeFrom/EVM.ts @@ -0,0 +1,79 @@ +// Bridge From EVM + +import { + BroSDK, + KnownTokenId, + KnownChainId, + toSDKNumberOrUndefined, + BridgeFromEVMInput, + EVMAddress, + SDKNumber, + formatSDKNumber, +} from "@brotocol-xyz/bro-sdk" + +// Choose your preferred web3 lib here +import { ethers } from "ethers" + +const sdk = new BroSDK() + +// For Stacks provide the contract address +const stacksToken: KnownTokenId.StacksToken = + (await sdk.stacksAddressToStacksToken(KnownChainId.Stacks.Mainnet, { + deployerAddress: "SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK", + contractName: "token-abtc", + }))! + +// For EVM tokens provide the contract address +const evmToken: KnownTokenId.EVMToken = (await sdk.evmAddressToEVMToken( + KnownChainId.EVM.Ethereum, + "0x31761a152F1e96F966C041291644129144233b0B", +))! + +// Example signer setup using ethers.js +const provider = new ethers.JsonRpcProvider( + "https://mainnet.someprovider.io/YOUR_PROJECT_ID", +) +const signer = new ethers.Wallet( + "000000000000000000000000000000000000000000000000000000000000002d", + provider, +) +const signerAddress = signer.address as `0x${string}` + +const bridgeFromEVMInput: BridgeFromEVMInput = { + fromChain: KnownChainId.EVM.Ethereum, + toChain: KnownChainId.Stacks.Mainnet, + fromToken: evmToken, + toToken: stacksToken, + // Sender Ethereum address + fromAddress: signerAddress, + // Receiver Stacks principal + toAddress: "SP2ZD731ANQZT6J4K3F5N8A40ZXWXC1XFXHVVQFKE", + amount: toSDKNumberOrUndefined(100), + sendTransaction: async (tx: { + from: EVMAddress // Sender Ethereum address + to: EVMAddress // Bridge Endpoint address + data: Uint8Array + recommendedGasLimit: SDKNumber + value?: SDKNumber + }): Promise<{ txHash: string }> => { + /** + * Implementation for sending transaction on Ethereum mainnet + * See https://docs.ethers.org/v6/api/contract/ for reference + */ + const txRequest = { + from: tx.from, + to: tx.to, + data: ethers.hexlify(tx.data), + gasLimit: formatSDKNumber(tx.recommendedGasLimit), + } + + const sentTx = await signer.sendTransaction(txRequest) + const receipt = await sentTx.wait() + if (receipt === null) throw new Error("Transaction receipt is null") + return { txHash: receipt.hash } + }, +} + +// Perform the bridge operation +const result = await sdk.bridgeFromEVM(bridgeFromEVMInput) +console.log("Transaction ID:", result.txHash) diff --git a/examples/bridgeFrom/Runes.ts b/examples/bridgeFrom/Runes.ts new file mode 100644 index 0000000..0e87e70 --- /dev/null +++ b/examples/bridgeFrom/Runes.ts @@ -0,0 +1,189 @@ +// Bridge From Runes +import { + GetConfirmedSpendableUTXOFn, + reselectSpendableUTXOsFactory, + UTXOBasic, +} from "@brotocol-xyz/bro-sdk/bitcoinHelpers" +import { + BroSDK, + KnownTokenId, + KnownChainId, + toSDKNumberOrUndefined, + BridgeFromRunesInput, + RunesUTXOSpendable, +} from "@brotocol-xyz/bro-sdk" +import { Psbt, payments, networks } from "bitcoinjs-lib" +import axios from "axios" + +const sdk = new BroSDK() + +// For Runes provide the runes ID +const runesToken: KnownTokenId.RunesToken = (await sdk.runesIdToRunesToken( + KnownChainId.Runes.Mainnet, + "500:20", +))! + +// For EVM tokens provide the contract address +const evmToken: KnownTokenId.EVMToken = (await sdk.evmAddressToEVMToken( + KnownChainId.EVM.Ethereum, + "0x31761a152F1e96F966C041291644129144233b0B", +))! + +// Mock functions for key management +// In a real implementation, these would be provided by the user's environment +const getPublicKey = async (): Promise => { + // This is a mock implementation + // In a real implementation, this would return the user's public key + return "02a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7" +} + +const signTx = async (hex: string): Promise => { + // This is a mock implementation + // In a real implementation, this would sign the transaction with the user's private key + return "mock_signature" +} + +// Get address and scriptPubKey +const publicKey = await getPublicKey() +const { address: senderAddress, output: scriptPubKey } = payments.p2wpkh({ + pubkey: Buffer.from(publicKey, "hex"), + network: networks.bitcoin, +}) + +// Select UTXOs to spend for network fees +const reselectSpendableNetworkFeeUTXOsForRunes: BridgeFromRunesInput["reselectSpendableNetworkFeeUTXOs"] = + async (satsToSend, lastTimeSelectedUTXOs) => { + // Example of available UTXOs from a Bitcoin node or API + const availableUTXOs: UTXOBasic[] = [ + { + txId: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + index: 0, + amount: 5000n, + }, + { + txId: "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", + index: 1, + amount: 3000n, + }, + { + txId: "7890abcdef1234567890abcdef1234567890abcdef1234567890abcdef123456", + index: 2, + amount: 2000n, + }, + ] + + // Function to convert basic UTXOs to spendable UTXOs with scriptPubKey + const getUTXOSpendable: GetConfirmedSpendableUTXOFn = async ( + utxo: UTXOBasic, + ) => { + // For this example, we'll create a simple UTXOSpendable object + return { + ...utxo, + scriptPubKey: scriptPubKey!, + addressType: "p2wpkh", + blockHeight: 800000n, // Example block height + } + } + + // Create the reselect function with factory helper + const reselectFn = reselectSpendableUTXOsFactory( + availableUTXOs, + getUTXOSpendable, + ) + + return reselectFn(satsToSend, lastTimeSelectedUTXOs) + } + +// Example Runes UTXOs +const runesUTXOs: RunesUTXOSpendable[] = [ + { + txId: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + index: 0, + amount: 1000n, + scriptPubKey: scriptPubKey!, + addressType: "p2wpkh", + runes: [ + { + runeId: "500:20", + runeDivisibility: 8, + runeAmount: 100000000n, // 1.0 rune + }, + ], + }, +] + +// Sign a Bitcoin PSBT for Runes +const signPsbtForRunes: BridgeFromRunesInput["signPsbt"] = async tx => { + const psbt = Psbt.fromBuffer(Buffer.from(tx.psbt)) + + // For each Bitcoin input that needs to be signed + for (const index of tx.signBitcoinInputs) { + // Sign the transaction using the mocked signTx function + const signature = await signTx(psbt.toHex()) + + // Add the signature to the PSBT + psbt.updateInput(index, { + partialSig: [ + { + pubkey: Buffer.from(await getPublicKey(), "hex"), + signature: Buffer.from(signature, "hex"), + }, + ], + }) + } + + // For each Runes input that needs to be signed + for (const index of tx.signRunesInputs) { + // Sign the transaction using the mocked signTx function + const signature = await signTx(psbt.toHex()) + + // Add the signature to the PSBT + psbt.updateInput(index, { + partialSig: [ + { + pubkey: Buffer.from(await getPublicKey(), "hex"), + signature: Buffer.from(signature, "hex"), + }, + ], + }) + } + + psbt.finalizeAllInputs() + return { psbt: psbt.toBuffer() } +} + +// Broadcast the signed transaction +const sendTransactionForRunes: BridgeFromRunesInput["sendTransaction"] = + async tx => { + const response = await axios.post( + "https://blockstream.info/api/tx", + tx.hex, + { + headers: { "Content-Type": "text/plain" }, + }, + ) + return { txid: response.data } + } + +// Create the bridge input +const bridgeFromRunesInput: BridgeFromRunesInput = { + fromChain: KnownChainId.Runes.Mainnet, + fromToken: runesToken, + toChain: KnownChainId.EVM.Ethereum, + toToken: evmToken, + fromAddress: senderAddress!, + fromAddressScriptPubKey: scriptPubKey!, + toAddress: "0x31751a152F1e95F966C041291644129144233b0B", + amount: toSDKNumberOrUndefined(1), // 1.0 rune + inputRuneUTXOs: runesUTXOs, + networkFeeRate: 10n, + reselectSpendableNetworkFeeUTXOs: reselectSpendableNetworkFeeUTXOsForRunes, + networkFeeChangeAddress: senderAddress!, + networkFeeChangeAddressScriptPubKey: scriptPubKey!, + signPsbt: signPsbtForRunes, + sendTransaction: sendTransactionForRunes, +} + +// Perform the bridge operation +const result = await sdk.bridgeFromRunes(bridgeFromRunesInput) +console.log("Bitcoin Transaction ID:", result.txid) diff --git a/examples/bridgeFrom/Stacks.ts b/examples/bridgeFrom/Stacks.ts new file mode 100644 index 0000000..023f9c5 --- /dev/null +++ b/examples/bridgeFrom/Stacks.ts @@ -0,0 +1,80 @@ +// Bridge From Stacks +import { + BroSDK, + KnownTokenId, + KnownChainId, + BridgeFromStacksInput, + toSDKNumberOrUndefined, + BridgeFromStacksInput_ContractCallOptions as ContractCallOptions, +} from "@brotocol-xyz/bro-sdk" +import { + makeContractCall, + broadcastTransaction, + deserializeCV, +} from "@stacks/transactions" + +const sdk = new BroSDK() + +// For Stacks provide the contract address +const stacksToken: KnownTokenId.StacksToken = + (await sdk.stacksAddressToStacksToken(KnownChainId.Stacks.Mainnet, { + deployerAddress: "SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK", + contractName: "token-abtc", + }))! + +// For EVM tokens provide the contract address +const evmToken: KnownTokenId.EVMToken = (await sdk.evmAddressToEVMToken( + KnownChainId.EVM.Ethereum, + "0x31761a152F1e96F966C041291644129144233b0B", +))! + +const bridgeInfo = await sdk.bridgeInfoFromStacks({ + fromChain: KnownChainId.Stacks.Mainnet, + toChain: KnownChainId.EVM.Ethereum, + fromToken: stacksToken, + toToken: evmToken, + amount: toSDKNumberOrUndefined(100_000_000), +}) + +console.log("Bridge Info:", bridgeInfo) + +const bridgeFromStacksInput: BridgeFromStacksInput = { + fromChain: KnownChainId.Stacks.Mainnet, + toChain: KnownChainId.EVM.Ethereum, + fromToken: stacksToken, + toToken: evmToken, + fromAddress: "SP2ZD731ANQZT6J4K3F5N8A40ZXWXC1XFXHVVQFKE", + // Receiver EVM address + toAddress: "0x31751a152F1e95F966C041291644129144233b0B", + amount: toSDKNumberOrUndefined(100), + sendTransaction: async (tx: ContractCallOptions) => { + /** + * Implementation for sending transaction on Stacks mainnet. + * Refer to: + * - https://github.com/hirosystems/stacks.js/tree/main/packages/transactions#smart-contract-function-call + * - https://stacks.js.org/functions/_stacks_transactions.makeContractCall + * - https://stacks.js.org/functions/_stacks_transactions.broadcastTransaction + */ + const transaction = await makeContractCall({ + contractAddress: tx.contractAddress, + contractName: tx.contractName, + functionName: tx.functionName, + // Deserialize each element of functionArgs and convert it into ClarityValue[] + functionArgs: tx.functionArgs.map(arg => deserializeCV(arg)), + postConditions: [] /* Add post conditions */, + validateWithAbi: true, + senderKey: + "b244296d5907de9864c0b0d51f98a13c52890be0404e83f273144cd5b9960eed01", + network: "mainnet", + }) + + const broadcastResponse = await broadcastTransaction({ + transaction, + network: "mainnet", + }) + return { txid: broadcastResponse.txid } + }, +} + +const result = await sdk.bridgeFromStacks(bridgeFromStacksInput) +console.log("Transaction ID:", result.txid) diff --git a/examples/bridgeFrom/package.json b/examples/bridgeFrom/package.json new file mode 100644 index 0000000..5b95876 --- /dev/null +++ b/examples/bridgeFrom/package.json @@ -0,0 +1,20 @@ +{ + "name": "bro-sdk-bridgeFrom-example", + "version": "1.0.0", + "description": "", + "private": true, + "main": null, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@brotocol-xyz/bro-sdk": "file:../..", + "@types/node": "^22.15.17", + "axios": "^1.9.0", + "bitcoinjs-lib": "^6.1.7", + "ethers": "^6.14.0" + } +} diff --git a/examples/bridgeFrom/pnpm-lock.yaml b/examples/bridgeFrom/pnpm-lock.yaml new file mode 100644 index 0000000..0b2f0f1 --- /dev/null +++ b/examples/bridgeFrom/pnpm-lock.yaml @@ -0,0 +1,791 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@brotocol-xyz/bro-sdk': + specifier: file:../.. + version: file:../..(@stacks/common@7.0.2) + '@types/node': + specifier: ^22.15.17 + version: 22.15.17 + axios: + specifier: ^1.9.0 + version: 1.9.0 + bitcoinjs-lib: + specifier: ^6.1.7 + version: 6.1.7 + ethers: + specifier: ^6.14.0 + version: 6.14.0 + +packages: + + '@adraffy/ens-normalize@1.10.1': + resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} + + '@brotocol-xyz/bro-sdk@file:../..': + resolution: {directory: ../.., type: directory} + + '@c4/btc-utils@0.3.1': + resolution: {integrity: sha512-mhJ43BhjJP7KspiWZVNnvCy3VTmN8EksBiV+d49jVPBDwrj8dBGuhbv3aM+AVnIfq4Z0Ds6k6q2wkl/lKeNcOA==} + + '@noble/curves@1.2.0': + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + + '@noble/curves@1.8.2': + resolution: {integrity: sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.0': + resolution: {integrity: sha512-7YDlXiNMdO1YZeH6t/kvopHHbIZzlxrCV9WLqCY6QhcXOoXiNCMDqJIglZ9Yjx5+w7Dz30TITFrlTjnRg7sKEg==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.1.5': + resolution: {integrity: sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ==} + + '@noble/hashes@1.3.2': + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + + '@noble/hashes@1.7.2': + resolution: {integrity: sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@noble/secp256k1@1.7.1': + resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} + + '@scure/base@1.2.5': + resolution: {integrity: sha512-9rE6EOVeIQzt5TSu4v+K523F8u6DhBsoZWPGKlnCshhlDhy0kJzUX4V+tr2dWmzF1GdekvThABoEQBGBQI7xZw==} + + '@scure/bip32@1.6.2': + resolution: {integrity: sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==} + + '@scure/bip39@1.5.4': + resolution: {integrity: sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==} + + '@scure/btc-signer@1.8.0': + resolution: {integrity: sha512-lzf9ugp2hZwP84bdRQuxdX2iib3wyUs7+8+Ph/hanVaXWGOZfSfgEZFaOyocj/Qh0Igt1WHkZh6hdh4KloynNQ==} + + '@stacks/common@7.0.2': + resolution: {integrity: sha512-+RSecHdkxOtswmE4tDDoZlYEuULpnTQVeDIG5eZ32opK8cFxf4EugAcK9CsIsHx/Se1yTEaQ21WGATmJGK84lQ==} + + '@stacks/network@7.0.2': + resolution: {integrity: sha512-XzHnoWqku/jRrTgMXhmh3c+I0O9vDH24KlhzGDZtBu+8CGGyHNPAZzGwvoUShonMXrXjEnfO9IYQwV5aJhfv6g==} + + '@stacks/stacks-blockchain-api-types@7.14.1': + resolution: {integrity: sha512-65hvhXxC+EUqHJAQsqlBCqXB+zwfxZICSKYJugdg6BCp9I9qniyfz5XyQeC4RMVo0tgEoRdS/b5ZCFo5kLWmxA==} + + '@stacks/transactions@7.0.6': + resolution: {integrity: sha512-qRGo4tNwOh+avUv/u4JGqqUWQ8xW/iUWtJV0o3BxpMyRxqDXmj+m+yeAEVYf9jRDouOo+NaWmwtRmWc0URZPdw==} + + '@types/node@22.15.17': + resolution: {integrity: sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==} + + '@types/node@22.7.5': + resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} + + abitype@1.0.8: + resolution: {integrity: sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + aes-js@4.0.0-beta.5: + resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.9.0: + resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==} + + base-x@4.0.1: + resolution: {integrity: sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==} + + bech32@2.0.0: + resolution: {integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==} + + big.js@6.2.2: + resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} + + bip174@2.1.1: + resolution: {integrity: sha512-mdFV5+/v0XyNYXjBS6CQPLo9ekCx4gtKZFnJm5PMto7Fs9hTTDpkkzOB7/FtluRI6JbUUAu+snTYfJRgHLZbZQ==} + engines: {node: '>=8.0.0'} + + bitcoinjs-lib@6.1.7: + resolution: {integrity: sha512-tlf/r2DGMbF7ky1MgUqXHzypYHakkEnm0SZP23CJKIqNY/5uNAnMbFhMJdhjrL/7anfb/U8+AlpdjPWjPnAalg==} + engines: {node: '>=8.0.0'} + + bs58@5.0.0: + resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} + + bs58check@3.0.1: + resolution: {integrity: sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ==} + + c32check@2.0.0: + resolution: {integrity: sha512-rpwfAcS/CMqo0oCqDf3r9eeLgScRE3l/xHDCXhM3UyrfvIn7PrLq63uHh7yYbv8NzaZn5MVsVhIRpQ+5GZ5HyA==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + clarity-codegen@1.1.3: + resolution: {integrity: sha512-/EXXaq5J8e9ldrQjsNPQwr2QOoIMBvLHFab5nahFwdfG18ZiyXSx9SvxHUbzMHBLUzsvgnlmopAlIj+R+2rwmw==} + hasBin: true + peerDependencies: + '@stacks/common': ^7.0.2 + '@stacks/transactions': ^7.0.2 + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + ethers@6.14.0: + resolution: {integrity: sha512-KgHwltNSMdbrGWEyKkM0Rt2s+u1nDH/5BVDQakLinzGEJi4bWindBzZSCC4gKsbZjwDTI6ex/8suR9Ihbmz4IQ==} + engines: {node: '>=14.0.0'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.2: + resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + engines: {node: '>= 6'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + isows@1.0.6: + resolution: {integrity: sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==} + peerDependencies: + ws: '*' + + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + micro-packed@0.7.3: + resolution: {integrity: sha512-2Milxs+WNC00TRlem41oRswvw31146GiSaoCT7s3Xi2gMUglW5QBeqlQaZeHr5tJx9nm3i57LNXPqxOOaWtTYg==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + ox@0.6.9: + resolution: {integrity: sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + + typeforce@1.18.0: + resolution: {integrity: sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + varuint-bitcoin@1.1.2: + resolution: {integrity: sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==} + + viem@2.29.2: + resolution: {integrity: sha512-cukRxab90jvQ+TDD84sU3qB3UmejYqgCw4cX8SfWzvh7JPfZXI3kAMUaT5OSR2As1Mgvx1EJawccwPjGqkSSwA==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.1: + resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yqueue@1.0.1: + resolution: {integrity: sha512-DBxJZBRafFLA/tCc5uO8ZTGFr+sQgn1FRJkZ4cVrIQIk6bv2bInraE3mbpLAJw9z93JGrLkqDoyTLrrZaCNq5w==} + +snapshots: + + '@adraffy/ens-normalize@1.10.1': {} + + '@brotocol-xyz/bro-sdk@file:../..(@stacks/common@7.0.2)': + dependencies: + '@c4/btc-utils': 0.3.1 + '@scure/btc-signer': 1.8.0 + '@stacks/network': 7.0.2 + '@stacks/transactions': 7.0.6 + big.js: 6.2.2 + c32check: 2.0.0 + clarity-codegen: 1.1.3(@stacks/common@7.0.2)(@stacks/transactions@7.0.6) + viem: 2.29.2 + transitivePeerDependencies: + - '@stacks/common' + - bufferutil + - debug + - encoding + - typescript + - utf-8-validate + - zod + + '@c4/btc-utils@0.3.1': + optionalDependencies: + '@scure/btc-signer': 1.8.0 + + '@noble/curves@1.2.0': + dependencies: + '@noble/hashes': 1.3.2 + + '@noble/curves@1.8.2': + dependencies: + '@noble/hashes': 1.7.2 + + '@noble/curves@1.9.0': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/hashes@1.1.5': {} + + '@noble/hashes@1.3.2': {} + + '@noble/hashes@1.7.2': {} + + '@noble/hashes@1.8.0': {} + + '@noble/secp256k1@1.7.1': {} + + '@scure/base@1.2.5': {} + + '@scure/bip32@1.6.2': + dependencies: + '@noble/curves': 1.8.2 + '@noble/hashes': 1.7.2 + '@scure/base': 1.2.5 + + '@scure/bip39@1.5.4': + dependencies: + '@noble/hashes': 1.7.2 + '@scure/base': 1.2.5 + + '@scure/btc-signer@1.8.0': + dependencies: + '@noble/curves': 1.9.0 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.5 + micro-packed: 0.7.3 + + '@stacks/common@7.0.2': {} + + '@stacks/network@7.0.2': + dependencies: + '@stacks/common': 7.0.2 + cross-fetch: 3.2.0 + transitivePeerDependencies: + - encoding + + '@stacks/stacks-blockchain-api-types@7.14.1': {} + + '@stacks/transactions@7.0.6': + dependencies: + '@noble/hashes': 1.1.5 + '@noble/secp256k1': 1.7.1 + '@stacks/common': 7.0.2 + '@stacks/network': 7.0.2 + c32check: 2.0.0 + lodash.clonedeep: 4.5.0 + transitivePeerDependencies: + - encoding + + '@types/node@22.15.17': + dependencies: + undici-types: 6.21.0 + + '@types/node@22.7.5': + dependencies: + undici-types: 6.19.8 + + abitype@1.0.8: {} + + aes-js@4.0.0-beta.5: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + asynckit@0.4.0: {} + + axios@1.9.0: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.2 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + base-x@4.0.1: {} + + bech32@2.0.0: {} + + big.js@6.2.2: {} + + bip174@2.1.1: {} + + bitcoinjs-lib@6.1.7: + dependencies: + '@noble/hashes': 1.8.0 + bech32: 2.0.0 + bip174: 2.1.1 + bs58check: 3.0.1 + typeforce: 1.18.0 + varuint-bitcoin: 1.1.2 + + bs58@5.0.0: + dependencies: + base-x: 4.0.1 + + bs58check@3.0.1: + dependencies: + '@noble/hashes': 1.8.0 + bs58: 5.0.0 + + c32check@2.0.0: + dependencies: + '@noble/hashes': 1.8.0 + base-x: 4.0.1 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + clarity-codegen@1.1.3(@stacks/common@7.0.2)(@stacks/transactions@7.0.6): + dependencies: + '@stacks/common': 7.0.2 + '@stacks/stacks-blockchain-api-types': 7.14.1 + '@stacks/transactions': 7.0.6 + axios: 1.9.0 + lodash: 4.17.21 + yargs: 17.7.2 + yqueue: 1.0.1 + transitivePeerDependencies: + - debug + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + delayed-stream@1.0.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + emoji-regex@8.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + escalade@3.2.0: {} + + ethers@6.14.0: + dependencies: + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@types/node': 22.7.5 + aes-js: 4.0.0-beta.5 + tslib: 2.7.0 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + eventemitter3@5.0.1: {} + + follow-redirects@1.15.9: {} + + form-data@4.0.2: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + mime-types: 2.1.35 + + function-bind@1.1.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + is-fullwidth-code-point@3.0.0: {} + + isows@1.0.6(ws@8.18.1): + dependencies: + ws: 8.18.1 + + lodash.clonedeep@4.5.0: {} + + lodash@4.17.21: {} + + math-intrinsics@1.1.0: {} + + micro-packed@0.7.3: + dependencies: + '@scure/base': 1.2.5 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + ox@0.6.9: + dependencies: + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.8.2 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + abitype: 1.0.8 + eventemitter3: 5.0.1 + transitivePeerDependencies: + - zod + + proxy-from-env@1.1.0: {} + + require-directory@2.1.1: {} + + safe-buffer@5.2.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + tr46@0.0.3: {} + + tslib@2.7.0: {} + + typeforce@1.18.0: {} + + undici-types@6.19.8: {} + + undici-types@6.21.0: {} + + varuint-bitcoin@1.1.2: + dependencies: + safe-buffer: 5.2.1 + + viem@2.29.2: + dependencies: + '@noble/curves': 1.8.2 + '@noble/hashes': 1.7.2 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + abitype: 1.0.8 + isows: 1.0.6(ws@8.18.1) + ox: 0.6.9 + ws: 8.18.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + ws@8.17.1: {} + + ws@8.18.1: {} + + y18n@5.0.8: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yqueue@1.0.1: {} diff --git a/examples/bridgeFrom/tsconfig.json b/examples/bridgeFrom/tsconfig.json new file mode 100644 index 0000000..5b63e0c --- /dev/null +++ b/examples/bridgeFrom/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["ESNext"], + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1852e9e..01cfd3a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -791,8 +791,8 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - axios@1.8.3: - resolution: {integrity: sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==} + axios@1.9.0: + resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -2373,7 +2373,7 @@ snapshots: asynckit@0.4.0: {} - axios@1.8.3: + axios@1.9.0: dependencies: follow-redirects: 1.15.9 form-data: 4.0.2 @@ -2443,7 +2443,7 @@ snapshots: '@stacks/common': 7.0.2 '@stacks/stacks-blockchain-api-types': 7.14.1 '@stacks/transactions': 7.0.5 - axios: 1.8.3 + axios: 1.9.0 lodash: 4.17.21 yargs: 17.7.2 yqueue: 1.0.1 diff --git a/src/sdkUtils/types.ts b/src/sdkUtils/types.ts index e154077..1f8f1dc 100644 --- a/src/sdkUtils/types.ts +++ b/src/sdkUtils/types.ts @@ -4,6 +4,14 @@ import { EVMEndpointContract } from "../evmUtils/evmContractAddresses" import { BigNumber } from "../utils/BigNumber" import { InvalidMethodParametersError } from "../utils/errors" +/** + * A branded literal type used for SDK-specific types `ChainId`, `TokenId` and `SDKNumber`. + * + * For example: + * - `"bitcoin-mainnet (BroSDK ChainId)"` represents a valid `ChainId` + * - `"brc20-something (BroSDK TokenId)"` represents a valid `TokenId` + * - `"10 (BroSDK number)"` represents a valid `SDKNumber` + */ type SDKBrandedLiteral< Type extends string, T extends string | number, diff --git a/tsconfig.json b/tsconfig.json index 78a9df6..56a1020 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,11 @@ { "$schema": "https://json.schemastore.org/tsconfig.json", - "exclude": ["node_modules", "lib", "examples/cross-chain-swap"], + "exclude": [ + "node_modules", + "lib", + "examples/cross-chain-swap", + "examples/bridgeFrom" + ], "compilerOptions": { "module": "esnext", "target": "esnext",