@xlink-network/xlink-sdk
Version:
187 lines (170 loc) • 6.01 kB
text/typescript
import { callReadOnlyFunction } from "@stacks/transactions"
import { CallReadOnlyFunctionFn, unwrapResponse } from "clarity-codegen"
import { toCorrespondingStacksToken } from "../evmUtils/peggingHelpers"
import { hasLength } from "../utils/arrayHelpers"
import { UnsupportedBridgeRouteError } from "../utils/errors"
import { decodeHex } from "../utils/hexHelpers"
import { checkNever } from "../utils/typeHelpers"
import { KnownChainId, KnownTokenId } from "../utils/types/knownIds"
import { StacksContractAddress } from "../xlinkSdkUtils/types"
import { contractAssignedChainIdFromKnownChain } from "./crossContractDataMapping"
import { getTerminatingStacksTokenContractAddress } from "./stxContractAddresses"
import {
addressToBuffer,
executeReadonlyCallXLINK,
getStacksContractCallInfo,
getStacksTokenContractInfo,
} from "./xlinkContractHelpers"
export interface BridgeSwapRouteNode {
poolId: bigint
tokenContractAddress: `${string}.${string}::${string}`
}
// prettier-ignore
export type BridgeSwapRoute_FromBitcoin =
| []
// | [BridgeSwapRouteNode]
// | [BridgeSwapRouteNode, BridgeSwapRouteNode]
// | [BridgeSwapRouteNode, BridgeSwapRouteNode, BridgeSwapRouteNode]
export interface CreateBridgeOrderResult {
terminatingStacksToken: StacksContractAddress
data: Uint8Array
}
export async function createBridgeOrder_BitcoinToStacks(info: {
fromChain: KnownChainId.BitcoinChain
fromBitcoinScriptPubKey: Uint8Array
toChain: KnownChainId.StacksChain
toToken: KnownTokenId.StacksToken
toStacksAddress: string
swapRoute: BridgeSwapRoute_FromBitcoin
swapSlippedAmount?: bigint
}): Promise<undefined | CreateBridgeOrderResult> {
let data: undefined | Uint8Array
const contractCallInfo = getStacksContractCallInfo(
info.toChain,
"btc-peg-in-endpoint-v2-05",
)
if (contractCallInfo == null) {
throw new UnsupportedBridgeRouteError(
info.fromChain,
info.toChain,
KnownTokenId.Bitcoin.BTC,
info.toToken,
)
}
const { swapRoute, toStacksAddress /*, swapSlippedAmount = 0n */ } = info
const executeOptions = {
deployerAddress: contractCallInfo.deployerAddress,
callReadOnlyFunction: (callOptions =>
callReadOnlyFunction({
...callOptions,
network: contractCallInfo.network,
})) satisfies CallReadOnlyFunctionFn,
}
const targetTokenContractInfo = getStacksTokenContractInfo(
info.toChain,
info.toToken,
)
if (targetTokenContractInfo == null) {
return undefined
}
if (hasLength(swapRoute, 0)) {
data = await executeReadonlyCallXLINK(
contractCallInfo.contractName,
"create-order-cross-or-fail",
{
order: {
from: info.fromBitcoinScriptPubKey,
to: addressToBuffer(info.toChain, toStacksAddress),
"chain-id": undefined,
token: `${targetTokenContractInfo.deployerAddress}.${targetTokenContractInfo.contractName}`,
"token-out": `${targetTokenContractInfo.deployerAddress}.${targetTokenContractInfo.contractName}`,
},
},
executeOptions,
).then(unwrapResponse)
} else {
checkNever(swapRoute)
}
return {
terminatingStacksToken: targetTokenContractInfo,
data: data!,
}
}
export async function createBridgeOrder_BitcoinToEVM(info: {
fromChain: KnownChainId.BitcoinChain
fromBitcoinScriptPubKey: Uint8Array
toChain: KnownChainId.EVMChain
toToken: KnownTokenId.EVMToken
toEVMAddress: string
swapRoute: BridgeSwapRoute_FromBitcoin
swapSlippedAmount?: bigint
}): Promise<undefined | CreateBridgeOrderResult> {
const contractCallInfo = getStacksContractCallInfo(
info.fromChain === KnownChainId.Bitcoin.Mainnet
? KnownChainId.Stacks.Mainnet
: KnownChainId.Stacks.Testnet,
"btc-peg-in-endpoint-v2-05",
)
if (contractCallInfo == null) {
throw new UnsupportedBridgeRouteError(
info.fromChain,
info.toChain,
KnownTokenId.Bitcoin.BTC,
info.toToken,
)
}
const { swapRoute, toEVMAddress /*, swapSlippedAmount = 0n */ } = info
const executeOptions = {
deployerAddress: contractCallInfo.deployerAddress,
callReadOnlyFunction: (callOptions =>
callReadOnlyFunction({
...callOptions,
network: contractCallInfo.network,
})) satisfies CallReadOnlyFunctionFn,
}
const targetChainId = contractAssignedChainIdFromKnownChain(info.toChain)
const swappedStacksToken = await toCorrespondingStacksToken(info.toToken)
if (swappedStacksToken == null) return undefined
const swappedStacksTokenAddress = getStacksTokenContractInfo(
contractCallInfo.network.isMainnet()
? KnownChainId.Stacks.Mainnet
: KnownChainId.Stacks.Testnet,
swappedStacksToken,
)
if (swappedStacksTokenAddress == null) return undefined
const terminatingStacksTokenAddress =
getTerminatingStacksTokenContractAddress({
fromChain: contractCallInfo.network.isMainnet()
? KnownChainId.Stacks.Mainnet
: KnownChainId.Stacks.Testnet,
fromToken: swappedStacksToken,
toChain: info.toChain,
toToken: info.toToken,
}) ?? swappedStacksTokenAddress
let data: undefined | Uint8Array
if (hasLength(swapRoute, 0)) {
data = await executeReadonlyCallXLINK(
contractCallInfo.contractName,
"create-order-cross-or-fail",
{
order: {
from: info.fromBitcoinScriptPubKey,
to: decodeHex(toEVMAddress),
"chain-id": targetChainId,
token: `${swappedStacksTokenAddress.deployerAddress}.${swappedStacksTokenAddress.contractName}`,
"token-out": `${terminatingStacksTokenAddress.deployerAddress}.${terminatingStacksTokenAddress.contractName}`,
},
},
executeOptions,
).then(unwrapResponse)
} else {
checkNever(swapRoute)
}
return {
terminatingStacksToken: {
deployerAddress: terminatingStacksTokenAddress.deployerAddress,
contractName: terminatingStacksTokenAddress.contractName,
},
data: data!,
}
}