@xlink-network/xlink-sdk
Version:
634 lines (632 loc) • 21.4 kB
JavaScript
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]],
[
[],
[],
[],
[]
]
),
...defineRoute(
// to Stacks
[[..._allKnownEVMMainnetChains], [KnownChainId.Stacks.Mainnet]],
[
// BTCs
[],
[],
[],
[],
// USDTs
[],
[],
// others
[],
[],
[],
[],
[],
[],
[],
[]
]
),
...defineRoute(
// to EVM
[[..._allKnownEVMMainnetChains], [..._allKnownEVMMainnetChains]],
[
// BTCs
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
// USDTs
[],
[],
[],
[],
// others
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
]
),
// from testnet
...defineRoute(
// to Bitcoin
[[..._allKnownEVMTestnetChains], [KnownChainId.Bitcoin.Testnet]],
[
[],
[],
[],
[]
]
),
...defineRoute(
// to Stacks
[[..._allKnownEVMTestnetChains], [KnownChainId.Stacks.Testnet]],
[
// BTCs
[],
[],
[],
[],
// USDTs
[],
[],
// others
[],
[],
[],
[],
[],
[],
[],
[]
]
),
...defineRoute(
// to EVM
[[..._allKnownEVMTestnetChains], [..._allKnownEVMTestnetChains]],
[
// BTCs
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
// USDTs
[],
[],
[],
[],
// others
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
]
)
],
{
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