UNPKG

@xlink-network/xlink-sdk

Version:
634 lines (632 loc) 21.4 kB
import { NativeBridgeEndpointAbi } from "./chunk-NPL4WHWU.mjs"; import { buildSupportedRoutes, defineRoute } from "./chunk-BSXD4QA3.mjs"; import { sendMessageAbi } from "./chunk-6COHCKZV.mjs"; import { isSupportedEVMRoute } from "./chunk-COUTRDKA.mjs"; import { BridgeEndpointAbi } from "./chunk-UNVARCOY.mjs"; import { getEVMContractCallInfo, getEVMTokenContractInfo, numberToSolidityContractNumber } from "./chunk-UJDCORME.mjs"; import { contractAssignedChainIdFromKnownChain } from "./chunk-RGOLYR47.mjs"; import { addressToBuffer, getStacksTokenContractInfo } from "./chunk-JJNS722O.mjs"; import { toSDKNumberOrUndefined } from "./chunk-FZQUIYHC.mjs"; import { nativeCurrencyAddress } from "./chunk-ZXKZ4Z37.mjs"; import { decodeHex, encodeZeroPrefixedHex } from "./chunk-UZKY7HXV.mjs"; import { InvalidMethodParametersError, UnsupportedBridgeRouteError } from "./chunk-KTUPNUM5.mjs"; import { KnownChainId, KnownTokenId, _allKnownEVMMainnetChains, _allKnownEVMTestnetChains, _knownChainIdToErrorMessagePart } from "./chunk-GZDQGY7B.mjs"; import { assertExclude, checkNever } from "./chunk-E5Y3FGR6.mjs"; import { BigNumber } from "./chunk-KHTJNOQE.mjs"; // src/xlinkSdkUtils/bridgeFromEVM.ts import { encodeFunctionData, toHex } from "viem"; import { estimateGas } from "viem/actions"; var supportedRoutes = buildSupportedRoutes( [ // from mainnet ...defineRoute( // to Bitcoin [[..._allKnownEVMMainnetChains], [KnownChainId.Bitcoin.Mainnet]], [ [KnownTokenId.EVM.aBTC, KnownTokenId.Bitcoin.BTC], [KnownTokenId.EVM.WBTC, KnownTokenId.Bitcoin.BTC], [KnownTokenId.EVM.BTCB, KnownTokenId.Bitcoin.BTC], [KnownTokenId.EVM.cbBTC, KnownTokenId.Bitcoin.BTC] ] ), ...defineRoute( // to Stacks [[..._allKnownEVMMainnetChains], [KnownChainId.Stacks.Mainnet]], [ // BTCs [KnownTokenId.EVM.aBTC, KnownTokenId.Stacks.aBTC], [KnownTokenId.EVM.WBTC, KnownTokenId.Stacks.aBTC], [KnownTokenId.EVM.BTCB, KnownTokenId.Stacks.aBTC], [KnownTokenId.EVM.cbBTC, KnownTokenId.Stacks.aBTC], // USDTs [KnownTokenId.EVM.USDT, KnownTokenId.Stacks.sUSDT], [KnownTokenId.EVM.sUSDT, KnownTokenId.Stacks.sUSDT], // others [KnownTokenId.EVM.SKO, KnownTokenId.Stacks.sSKO], [KnownTokenId.EVM.ALEX, KnownTokenId.Stacks.ALEX], [KnownTokenId.EVM.vLiSTX, KnownTokenId.Stacks.vLiSTX], [KnownTokenId.EVM.vLiALEX, KnownTokenId.Stacks.vLiALEX], [KnownTokenId.EVM.uBTC, KnownTokenId.Stacks.uBTC], [KnownTokenId.EVM.wuBTC, KnownTokenId.Stacks.uBTC], [KnownTokenId.EVM.DB20, KnownTokenId.Stacks.DB20], [KnownTokenId.EVM.DOG, KnownTokenId.Stacks.DOG] ] ), ...defineRoute( // to EVM [[..._allKnownEVMMainnetChains], [..._allKnownEVMMainnetChains]], [ // BTCs [KnownTokenId.EVM.WBTC, KnownTokenId.EVM.aBTC], [KnownTokenId.EVM.aBTC, KnownTokenId.EVM.WBTC], [KnownTokenId.EVM.WBTC, KnownTokenId.EVM.BTCB], [KnownTokenId.EVM.BTCB, KnownTokenId.EVM.WBTC], [KnownTokenId.EVM.WBTC, KnownTokenId.EVM.cbBTC], [KnownTokenId.EVM.cbBTC, KnownTokenId.EVM.WBTC], [KnownTokenId.EVM.BTCB, KnownTokenId.EVM.aBTC], [KnownTokenId.EVM.aBTC, KnownTokenId.EVM.BTCB], [KnownTokenId.EVM.BTCB, KnownTokenId.EVM.cbBTC], [KnownTokenId.EVM.cbBTC, KnownTokenId.EVM.BTCB], [KnownTokenId.EVM.cbBTC, KnownTokenId.EVM.aBTC], [KnownTokenId.EVM.aBTC, KnownTokenId.EVM.cbBTC], [KnownTokenId.EVM.WBTC, KnownTokenId.EVM.WBTC], [KnownTokenId.EVM.BTCB, KnownTokenId.EVM.BTCB], [KnownTokenId.EVM.aBTC, KnownTokenId.EVM.aBTC], [KnownTokenId.EVM.cbBTC, KnownTokenId.EVM.cbBTC], // USDTs [KnownTokenId.EVM.sUSDT, KnownTokenId.EVM.USDT], [KnownTokenId.EVM.USDT, KnownTokenId.EVM.sUSDT], [KnownTokenId.EVM.USDT, KnownTokenId.EVM.USDT], [KnownTokenId.EVM.sUSDT, KnownTokenId.EVM.sUSDT], // others [KnownTokenId.EVM.SKO, KnownTokenId.EVM.SKO], [KnownTokenId.EVM.ALEX, KnownTokenId.EVM.ALEX], [KnownTokenId.EVM.vLiSTX, KnownTokenId.EVM.vLiSTX], [KnownTokenId.EVM.vLiALEX, KnownTokenId.EVM.vLiALEX], [KnownTokenId.EVM.uBTC, KnownTokenId.EVM.uBTC], [KnownTokenId.EVM.wuBTC, KnownTokenId.EVM.wuBTC], [KnownTokenId.EVM.uBTC, KnownTokenId.EVM.wuBTC], [KnownTokenId.EVM.wuBTC, KnownTokenId.EVM.uBTC], [KnownTokenId.EVM.DB20, KnownTokenId.EVM.DB20], [KnownTokenId.EVM.DOG, KnownTokenId.EVM.DOG] ] ), // from testnet ...defineRoute( // to Bitcoin [[..._allKnownEVMTestnetChains], [KnownChainId.Bitcoin.Testnet]], [ [KnownTokenId.EVM.aBTC, KnownTokenId.Bitcoin.BTC], [KnownTokenId.EVM.WBTC, KnownTokenId.Bitcoin.BTC], [KnownTokenId.EVM.BTCB, KnownTokenId.Bitcoin.BTC], [KnownTokenId.EVM.cbBTC, KnownTokenId.Bitcoin.BTC] ] ), ...defineRoute( // to Stacks [[..._allKnownEVMTestnetChains], [KnownChainId.Stacks.Testnet]], [ // BTCs [KnownTokenId.EVM.aBTC, KnownTokenId.Stacks.aBTC], [KnownTokenId.EVM.WBTC, KnownTokenId.Stacks.aBTC], [KnownTokenId.EVM.BTCB, KnownTokenId.Stacks.aBTC], [KnownTokenId.EVM.cbBTC, KnownTokenId.Stacks.aBTC], // USDTs [KnownTokenId.EVM.USDT, KnownTokenId.Stacks.sUSDT], [KnownTokenId.EVM.sUSDT, KnownTokenId.Stacks.sUSDT], // others [KnownTokenId.EVM.SKO, KnownTokenId.Stacks.sSKO], [KnownTokenId.EVM.ALEX, KnownTokenId.Stacks.ALEX], [KnownTokenId.EVM.vLiSTX, KnownTokenId.Stacks.vLiSTX], [KnownTokenId.EVM.vLiALEX, KnownTokenId.Stacks.vLiALEX], [KnownTokenId.EVM.uBTC, KnownTokenId.Stacks.uBTC], [KnownTokenId.EVM.wuBTC, KnownTokenId.Stacks.uBTC], [KnownTokenId.EVM.DB20, KnownTokenId.Stacks.DB20], [KnownTokenId.EVM.DOG, KnownTokenId.Stacks.DOG] ] ), ...defineRoute( // to EVM [[..._allKnownEVMTestnetChains], [..._allKnownEVMTestnetChains]], [ // BTCs [KnownTokenId.EVM.WBTC, KnownTokenId.EVM.aBTC], [KnownTokenId.EVM.aBTC, KnownTokenId.EVM.WBTC], [KnownTokenId.EVM.WBTC, KnownTokenId.EVM.BTCB], [KnownTokenId.EVM.BTCB, KnownTokenId.EVM.WBTC], [KnownTokenId.EVM.WBTC, KnownTokenId.EVM.cbBTC], [KnownTokenId.EVM.cbBTC, KnownTokenId.EVM.WBTC], [KnownTokenId.EVM.BTCB, KnownTokenId.EVM.aBTC], [KnownTokenId.EVM.aBTC, KnownTokenId.EVM.BTCB], [KnownTokenId.EVM.BTCB, KnownTokenId.EVM.cbBTC], [KnownTokenId.EVM.cbBTC, KnownTokenId.EVM.BTCB], [KnownTokenId.EVM.cbBTC, KnownTokenId.EVM.aBTC], [KnownTokenId.EVM.aBTC, KnownTokenId.EVM.cbBTC], [KnownTokenId.EVM.WBTC, KnownTokenId.EVM.WBTC], [KnownTokenId.EVM.BTCB, KnownTokenId.EVM.BTCB], [KnownTokenId.EVM.aBTC, KnownTokenId.EVM.aBTC], [KnownTokenId.EVM.cbBTC, KnownTokenId.EVM.cbBTC], // USDTs [KnownTokenId.EVM.sUSDT, KnownTokenId.EVM.USDT], [KnownTokenId.EVM.USDT, KnownTokenId.EVM.sUSDT], [KnownTokenId.EVM.USDT, KnownTokenId.EVM.USDT], [KnownTokenId.EVM.sUSDT, KnownTokenId.EVM.sUSDT], // others [KnownTokenId.EVM.SKO, KnownTokenId.EVM.SKO], [KnownTokenId.EVM.ALEX, KnownTokenId.EVM.ALEX], [KnownTokenId.EVM.vLiSTX, KnownTokenId.EVM.vLiSTX], [KnownTokenId.EVM.vLiALEX, KnownTokenId.EVM.vLiALEX], [KnownTokenId.EVM.uBTC, KnownTokenId.EVM.uBTC], [KnownTokenId.EVM.wuBTC, KnownTokenId.EVM.wuBTC], [KnownTokenId.EVM.uBTC, KnownTokenId.EVM.wuBTC], [KnownTokenId.EVM.wuBTC, KnownTokenId.EVM.uBTC], [KnownTokenId.EVM.DB20, KnownTokenId.EVM.DB20], [KnownTokenId.EVM.DOG, KnownTokenId.EVM.DOG] ] ) ], { isSupported: isSupportedEVMRoute } ); async function bridgeFromEVM(ctx, info) { const route = await supportedRoutes.checkRouteValid(ctx, info); if (KnownChainId.isEVMChain(route.fromChain)) { if (KnownChainId.isStacksChain(route.toChain)) { if (KnownTokenId.isEVMToken(route.fromToken) && KnownTokenId.isStacksToken(route.toToken)) { return bridgeFromEVM_toStacks(ctx, { ...info, fromChain: route.fromChain, toChain: route.toChain, fromToken: route.fromToken, toToken: route.toToken }); } } else if (KnownChainId.isBitcoinChain(route.toChain)) { if (KnownTokenId.isEVMToken(route.fromToken) && KnownTokenId.isBitcoinToken(route.toToken)) { return bridgeFromEVM_toBitcoin(ctx, { ...info, fromChain: route.fromChain, toChain: route.toChain, fromToken: route.fromToken, toToken: route.toToken }); } } else if (KnownChainId.isEVMChain(route.toChain)) { if (KnownTokenId.isEVMToken(route.fromToken) && KnownTokenId.isEVMToken(route.toToken)) { return bridgeFromEVM_toEVM(ctx, { ...info, fromChain: route.fromChain, toChain: route.toChain, fromToken: route.fromToken, toToken: route.toToken }); } } else if (KnownChainId.isBRC20Chain(route.toChain)) { if (KnownTokenId.isEVMToken(route.fromToken) && KnownTokenId.isBRC20Token(route.toToken)) { return bridgeFromEVM_toMeta(ctx, { ...info, fromChain: route.fromChain, toChain: route.toChain, fromToken: route.fromToken, toToken: route.toToken }); } } else if (KnownChainId.isRunesChain(route.toChain)) { if (KnownTokenId.isEVMToken(route.fromToken) && KnownTokenId.isRunesToken(route.toToken)) { return bridgeFromEVM_toMeta(ctx, { ...info, fromChain: route.fromChain, toChain: route.toChain, fromToken: route.fromToken, toToken: route.toToken }); } } else { checkNever(route.toChain); } } else { assertExclude(route.fromChain, assertExclude.i()); assertExclude(route.fromChain, assertExclude.i()); checkNever(route); } throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, info.fromToken, info.toToken ); } async function bridgeFromEVM_toStacks(ctx, info) { const { bridgeEndpointContractAddress: bridgeEndpointAddress } = await getEVMContractCallInfo(ctx, info.fromChain) ?? {}; const fromTokenContractInfo = await getEVMTokenContractInfo( ctx, info.fromChain, info.fromToken ); const toTokenContractInfo = await getStacksTokenContractInfo( info.toChain, info.toToken ); if (bridgeEndpointAddress == null || fromTokenContractInfo == null || toTokenContractInfo == null || fromTokenContractInfo.tokenContractAddress === nativeCurrencyAddress) { throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, info.fromToken, info.toToken ); } const message = await encodeFunctionData({ abi: sendMessageAbi, functionName: "transferToStacks", args: [info.toAddress] }); const functionData = await encodeFunctionData({ abi: BridgeEndpointAbi, functionName: "sendMessageWithToken", args: [ fromTokenContractInfo.tokenContractAddress, numberToSolidityContractNumber(info.amount), message ] }); const fallbackGasLimit = 2e5; const estimated = await estimateGas(fromTokenContractInfo.client, { account: info.fromAddress, to: bridgeEndpointAddress, data: functionData }).then( (n) => BigNumber.round( { precision: 0 }, BigNumber.max([fallbackGasLimit, BigNumber.mul(n, 1.2)]) ) ).catch( // add a fallback in case estimate failed () => fallbackGasLimit ); return await info.sendTransaction({ from: info.fromAddress, to: bridgeEndpointAddress, data: decodeHex(functionData), recommendedGasLimit: toSDKNumberOrUndefined(estimated) }); } async function bridgeFromEVM_toBitcoin(ctx, info) { const { bridgeEndpointContractAddress, nativeBridgeEndpointContractAddress } = await getEVMContractCallInfo(ctx, info.fromChain) ?? {}; const fromTokenContractInfo = await getEVMTokenContractInfo( ctx, info.fromChain, info.fromToken ); if (fromTokenContractInfo == null) { throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, info.fromToken, info.toToken ); } if (info.toAddressScriptPubKey == null) { throw new InvalidMethodParametersError( ["XLinkSDK", "bridgeFromEVM (to Bitcoin)"], [ { name: "toAddressScriptPubKey", expected: "Uint8Array", received: "undefined" } ] ); } const toAddressHex = toHex(info.toAddressScriptPubKey); const message = await encodeFunctionData({ abi: sendMessageAbi, functionName: "transferToBTC", args: [toAddressHex] }); let bridgeEndpointAddress; let functionData; let value; if (fromTokenContractInfo.tokenContractAddress === nativeCurrencyAddress) { functionData = await encodeFunctionData({ abi: NativeBridgeEndpointAbi, functionName: "withdraw", args: [ encodeZeroPrefixedHex(addressToBuffer(info.toChain, info.toAddress)) ] }); const nativeCurrencyDecimals = fromTokenContractInfo.client.chain?.nativeCurrency.decimals; if (nativeCurrencyDecimals == null) { throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, info.fromToken, info.toToken ); } bridgeEndpointAddress = nativeBridgeEndpointContractAddress; value = toSDKNumberOrUndefined( BigNumber.rightMoveDecimals(nativeCurrencyDecimals, info.amount) ); } else { functionData = await encodeFunctionData({ abi: BridgeEndpointAbi, functionName: "sendMessageWithToken", args: [ fromTokenContractInfo.tokenContractAddress, numberToSolidityContractNumber(info.amount), message ] }); bridgeEndpointAddress = bridgeEndpointContractAddress; } if (bridgeEndpointAddress == null) { throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, info.fromToken, info.toToken ); } const fallbackGasLimit = 2e5; const estimated = await estimateGas(fromTokenContractInfo.client, { account: info.fromAddress, to: bridgeEndpointAddress, data: functionData }).then( (n) => BigNumber.round( { precision: 0 }, BigNumber.max([fallbackGasLimit, BigNumber.mul(n, 1.2)]) ) ).catch( // add a fallback in case estimate failed () => fallbackGasLimit ); return await info.sendTransaction({ from: info.fromAddress, to: bridgeEndpointAddress, data: decodeHex(functionData), value, recommendedGasLimit: toSDKNumberOrUndefined(estimated) }); } async function bridgeFromEVM_toEVM(ctx, info) { const { bridgeEndpointContractAddress: bridgeEndpointAddress } = await getEVMContractCallInfo(ctx, info.fromChain) ?? {}; const fromTokenContractInfo = await getEVMTokenContractInfo( ctx, info.fromChain, info.fromToken ); const toTokenContractInfo = await getEVMTokenContractInfo( ctx, info.toChain, info.toToken ); if (bridgeEndpointAddress == null || fromTokenContractInfo == null || toTokenContractInfo == null || fromTokenContractInfo.tokenContractAddress === nativeCurrencyAddress) { throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, info.fromToken, info.toToken ); } const message = await encodeFunctionData({ abi: sendMessageAbi, functionName: "transferToEVM", args: [ contractAssignedChainIdFromKnownChain(info.toChain), toTokenContractInfo.tokenContractAddress, info.toAddress ] }); const functionData = await encodeFunctionData({ abi: BridgeEndpointAbi, functionName: "sendMessageWithToken", args: [ fromTokenContractInfo.tokenContractAddress, numberToSolidityContractNumber(info.amount), message ] }); const fallbackGasLimit = 2e5; const estimated = await estimateGas(fromTokenContractInfo.client, { account: info.fromAddress, to: bridgeEndpointAddress, data: functionData }).then( (n) => BigNumber.round( { precision: 0 }, BigNumber.max([fallbackGasLimit, BigNumber.mul(n, 1.2)]) ) ).catch( // add a fallback in case estimate failed () => fallbackGasLimit ); return await info.sendTransaction({ from: info.fromAddress, to: bridgeEndpointAddress, data: decodeHex(functionData), recommendedGasLimit: toSDKNumberOrUndefined(estimated) }); } async function bridgeFromEVM_toMeta(ctx, info) { const { bridgeEndpointContractAddress: bridgeEndpointAddress } = await getEVMContractCallInfo(ctx, info.fromChain) ?? {}; const fromTokenContractInfo = await getEVMTokenContractInfo( ctx, info.fromChain, info.fromToken ); if (bridgeEndpointAddress == null || fromTokenContractInfo == null || fromTokenContractInfo.tokenContractAddress === nativeCurrencyAddress) { throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, info.fromToken, info.toToken ); } if (info.toAddressScriptPubKey == null) { throw new InvalidMethodParametersError( [ "XLinkSDK", `bridgeFromEVM (to ${KnownChainId.isBRC20Chain(info.toChain) ? "BRC20" : "Runes"})` ], [ { name: "toAddressScriptPubKey", expected: "Uint8Array", received: "undefined" } ] ); } const toAddressHex = toHex(info.toAddressScriptPubKey); const message = await encodeFunctionData({ abi: sendMessageAbi, functionName: KnownChainId.isBRC20Chain(info.toChain) ? "transferToBRC20" : "transferToRunes", args: [toAddressHex] }); const functionData = await encodeFunctionData({ abi: BridgeEndpointAbi, functionName: "sendMessageWithToken", args: [ fromTokenContractInfo.tokenContractAddress, numberToSolidityContractNumber(info.amount), message ] }); const fallbackGasLimit = 2e5; const estimated = await estimateGas(fromTokenContractInfo.client, { account: info.fromAddress, to: bridgeEndpointAddress, data: functionData }).then( (n) => BigNumber.round( { precision: 0 }, BigNumber.max([fallbackGasLimit, BigNumber.mul(n, 1.2)]) ) ).catch( // add a fallback in case estimate failed () => fallbackGasLimit ); return await info.sendTransaction({ from: info.fromAddress, to: bridgeEndpointAddress, data: decodeHex(functionData), recommendedGasLimit: toSDKNumberOrUndefined(estimated) }); } async function bridgeFromEVM_toLaunchpad(ctx, info) { const { bridgeEndpointContractAddress: bridgeEndpointAddress } = await getEVMContractCallInfo(ctx, info.fromChain) ?? {}; const fromTokenContractInfo = await getEVMTokenContractInfo( ctx, info.fromChain, info.fromToken ); if (bridgeEndpointAddress == null || fromTokenContractInfo == null || fromTokenContractInfo.tokenContractAddress === nativeCurrencyAddress) { throw new UnsupportedBridgeRouteError( info.fromChain, info.receiverChain, info.fromToken ); } if (KnownChainId.isBitcoinChain(info.receiverChain) && info.receiverAddressScriptPubKey == null) { throw new InvalidMethodParametersError( [ "XLinkSDK", `bridgeFromEVM_toLaunchpad (to ${_knownChainIdToErrorMessagePart(info.receiverChain)})` ], [ { name: "toAddressScriptPubKey", expected: "Uint8Array", received: "undefined" } ] ); } const toAddressHex = info.receiverAddressScriptPubKey != null ? toHex(info.receiverAddressScriptPubKey) : toHex(addressToBuffer(info.receiverChain, info.receiverAddress)); const message = await encodeFunctionData({ abi: sendMessageAbi, functionName: "transferToLaunchpad", args: [ BigNumber.toBigInt({ roundingMode: BigNumber.roundDown }, info.launchId), toAddressHex ] }); const functionData = await encodeFunctionData({ abi: BridgeEndpointAbi, functionName: "sendMessageWithToken", args: [ fromTokenContractInfo.tokenContractAddress, numberToSolidityContractNumber(info.amount), message ] }); const fallbackGasLimit = 2e5; const estimated = await estimateGas(fromTokenContractInfo.client, { account: info.fromAddress, to: bridgeEndpointAddress, data: functionData }).then( (n) => BigNumber.round( { precision: 0 }, BigNumber.max([fallbackGasLimit, BigNumber.mul(n, 1.2)]) ) ).catch( // add a fallback in case estimate failed () => fallbackGasLimit ); return await info.sendTransaction({ from: info.fromAddress, to: bridgeEndpointAddress, data: decodeHex(functionData), recommendedGasLimit: toSDKNumberOrUndefined(estimated) }); } export { supportedRoutes, bridgeFromEVM, bridgeFromEVM_toLaunchpad }; //# sourceMappingURL=chunk-JQWEO2ZM.mjs.map