mirror of
https://github.com/Brotocol-xyz/bro-sdk.git
synced 2026-01-12 06:44:18 +08:00
feat: implement cross chain swap
This commit is contained in:
@@ -0,0 +1,243 @@
|
|||||||
|
|
||||||
|
import {
|
||||||
|
defineContract,
|
||||||
|
principalT,
|
||||||
|
bufferT,
|
||||||
|
responseSimpleT,
|
||||||
|
booleanT,
|
||||||
|
tupleT,
|
||||||
|
uintT,
|
||||||
|
listT,
|
||||||
|
traitT,
|
||||||
|
optionalT,
|
||||||
|
stringAsciiT,
|
||||||
|
noneT
|
||||||
|
} from "../smartContractHelpers/codegenImport"
|
||||||
|
|
||||||
|
export const btcPegInEndpointV207Swap = defineContract({
|
||||||
|
"btc-peg-in-endpoint-v2-07-swap": {
|
||||||
|
callback: {
|
||||||
|
input: [
|
||||||
|
{ name: 'sender', type: principalT },
|
||||||
|
{ name: 'payload', type: bufferT }
|
||||||
|
],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'finalize-peg-in-cross-swap': {
|
||||||
|
input: [
|
||||||
|
{ name: 'tx', type: bufferT },
|
||||||
|
{
|
||||||
|
name: 'block',
|
||||||
|
type: tupleT({ header: bufferT, height: uintT }, )
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'proof',
|
||||||
|
type: tupleT({ hashes: listT(bufferT, ), 'tree-depth': uintT, 'tx-index': uintT }, )
|
||||||
|
},
|
||||||
|
{ name: 'output-idx', type: uintT },
|
||||||
|
{
|
||||||
|
name: 'reveal-tx',
|
||||||
|
type: tupleT({ 'order-idx': uintT, tx: bufferT }, )
|
||||||
|
},
|
||||||
|
{ name: 'routing-traits', type: listT(traitT, ) },
|
||||||
|
{ name: 'token-out-trait', type: traitT }
|
||||||
|
],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'pause-peg-in': {
|
||||||
|
input: [ { name: 'paused', type: booleanT } ],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'set-btc-peg-out-fee': {
|
||||||
|
input: [ { name: 'fee', type: uintT } ],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'set-btc-peg-out-min-fee': {
|
||||||
|
input: [ { name: 'fee', type: uintT } ],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'set-fee-to-address': {
|
||||||
|
input: [ { name: 'new-fee-to-address', type: principalT } ],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'set-peg-in-fee': {
|
||||||
|
input: [ { name: 'fee', type: uintT } ],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'set-peg-in-min-fee': {
|
||||||
|
input: [ { name: 'fee', type: uintT } ],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'break-routing-id': {
|
||||||
|
input: [ { name: 'routing-ids', type: listT(uintT, ) } ],
|
||||||
|
output: responseSimpleT(tupleT({
|
||||||
|
'routing-factors': listT(uintT, ),
|
||||||
|
'routing-tokens': listT(principalT, )
|
||||||
|
}, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'construct-principal': {
|
||||||
|
input: [ { name: 'hash-bytes', type: bufferT } ],
|
||||||
|
output: responseSimpleT(principalT, ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'create-order-cross-swap-or-fail': {
|
||||||
|
input: [
|
||||||
|
{
|
||||||
|
name: 'order',
|
||||||
|
type: tupleT({
|
||||||
|
'chain-id': optionalT(uintT, ),
|
||||||
|
from: bufferT,
|
||||||
|
'min-amount-out': optionalT(uintT, ),
|
||||||
|
routing: listT(uintT, ),
|
||||||
|
to: bufferT,
|
||||||
|
'token-out': principalT
|
||||||
|
}, )
|
||||||
|
}
|
||||||
|
],
|
||||||
|
output: responseSimpleT(bufferT, ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'decode-from-reveal-tx-or-fail': {
|
||||||
|
input: [
|
||||||
|
{ name: 'tx', type: bufferT },
|
||||||
|
{ name: 'order-idx', type: uintT }
|
||||||
|
],
|
||||||
|
output: responseSimpleT(tupleT({ 'commit-txid': bufferT, 'order-script': bufferT }, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'decode-order-cross-swap-from-reveal-tx-or-fail': {
|
||||||
|
input: [
|
||||||
|
{ name: 'tx', type: bufferT },
|
||||||
|
{ name: 'order-idx', type: uintT }
|
||||||
|
],
|
||||||
|
output: responseSimpleT(tupleT({
|
||||||
|
'commit-txid': bufferT,
|
||||||
|
'order-details': tupleT({
|
||||||
|
'chain-id': optionalT(uintT, ),
|
||||||
|
from: bufferT,
|
||||||
|
'min-amount-out': optionalT(uintT, ),
|
||||||
|
routing: listT(uintT, ),
|
||||||
|
to: bufferT,
|
||||||
|
'token-out': principalT
|
||||||
|
}, )
|
||||||
|
}, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'decode-order-cross-swap-or-fail': {
|
||||||
|
input: [ { name: 'order-script', type: bufferT } ],
|
||||||
|
output: responseSimpleT(tupleT({
|
||||||
|
'chain-id': optionalT(uintT, ),
|
||||||
|
from: bufferT,
|
||||||
|
'min-amount-out': optionalT(uintT, ),
|
||||||
|
routing: listT(uintT, ),
|
||||||
|
to: bufferT,
|
||||||
|
'token-out': principalT
|
||||||
|
}, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'destruct-principal': {
|
||||||
|
input: [ { name: 'address', type: principalT } ],
|
||||||
|
output: responseSimpleT(tupleT({
|
||||||
|
'hash-bytes': bufferT,
|
||||||
|
name: optionalT(stringAsciiT, ),
|
||||||
|
version: bufferT
|
||||||
|
}, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'extract-tx-ins-outs': {
|
||||||
|
input: [ { name: 'tx', type: bufferT } ],
|
||||||
|
output: responseSimpleT(tupleT({
|
||||||
|
ins: listT(tupleT({
|
||||||
|
outpoint: tupleT({ hash: bufferT, index: uintT }, ),
|
||||||
|
scriptSig: bufferT,
|
||||||
|
sequence: uintT
|
||||||
|
}, ), ),
|
||||||
|
outs: listT(tupleT({ scriptPubKey: bufferT, value: uintT }, ), )
|
||||||
|
}, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'get-btc-peg-out-fee': { input: [], output: uintT, mode: 'readonly' },
|
||||||
|
'get-btc-peg-out-min-fee': { input: [], output: uintT, mode: 'readonly' },
|
||||||
|
'get-fee-to-address': { input: [], output: principalT, mode: 'readonly' },
|
||||||
|
'get-peg-in-fee': { input: [], output: uintT, mode: 'readonly' },
|
||||||
|
'get-peg-in-min-fee': { input: [], output: uintT, mode: 'readonly' },
|
||||||
|
'get-peg-in-sent-or-default': {
|
||||||
|
input: [ { name: 'tx', type: bufferT }, { name: 'output', type: uintT } ],
|
||||||
|
output: booleanT,
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'get-txid': {
|
||||||
|
input: [ { name: 'tx', type: bufferT } ],
|
||||||
|
output: responseSimpleT(bufferT, ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'is-dao-or-extension': { input: [], output: responseSimpleT(booleanT, ), mode: 'readonly' },
|
||||||
|
'is-peg-in-address-approved': {
|
||||||
|
input: [ { name: 'address', type: bufferT } ],
|
||||||
|
output: booleanT,
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'is-peg-in-paused': { input: [], output: booleanT, mode: 'readonly' },
|
||||||
|
'validate-tx-cross-swap': {
|
||||||
|
input: [
|
||||||
|
{
|
||||||
|
name: 'commit-tx',
|
||||||
|
type: tupleT({ 'output-idx': uintT, tx: bufferT }, )
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reveal-tx',
|
||||||
|
type: tupleT({ 'order-idx': uintT, tx: bufferT }, )
|
||||||
|
},
|
||||||
|
{ name: 'routing-traits', type: listT(traitT, ) },
|
||||||
|
{ name: 'token-out-trait', type: traitT }
|
||||||
|
],
|
||||||
|
output: responseSimpleT(tupleT({
|
||||||
|
'amount-net': uintT,
|
||||||
|
fee: uintT,
|
||||||
|
'order-details': tupleT({
|
||||||
|
'chain-id': optionalT(uintT, ),
|
||||||
|
from: bufferT,
|
||||||
|
'min-amount-out': optionalT(uintT, ),
|
||||||
|
routing: listT(uintT, ),
|
||||||
|
to: bufferT,
|
||||||
|
'token-out': principalT
|
||||||
|
}, ),
|
||||||
|
'routing-factors': listT(uintT, ),
|
||||||
|
'routing-tokens': listT(principalT, )
|
||||||
|
}, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'verify-mined': {
|
||||||
|
input: [
|
||||||
|
{ name: 'tx', type: bufferT },
|
||||||
|
{
|
||||||
|
name: 'block',
|
||||||
|
type: tupleT({ header: bufferT, height: uintT }, )
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'proof',
|
||||||
|
type: tupleT({ hashes: listT(bufferT, ), 'tree-depth': uintT, 'tx-index': uintT }, )
|
||||||
|
}
|
||||||
|
],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'btc-peg-out-min-fee': { input: noneT, output: uintT, mode: 'variable' },
|
||||||
|
'btc-peg-outfee': { input: noneT, output: uintT, mode: 'variable' },
|
||||||
|
'fee-to-address': { input: noneT, output: principalT, mode: 'variable' },
|
||||||
|
'peg-in-fee': { input: noneT, output: uintT, mode: 'variable' },
|
||||||
|
'peg-in-min-fee': { input: noneT, output: uintT, mode: 'variable' },
|
||||||
|
'peg-in-paused': { input: noneT, output: booleanT, mode: 'variable' }
|
||||||
|
}
|
||||||
|
} as const)
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,330 @@
|
|||||||
|
|
||||||
|
import {
|
||||||
|
defineContract,
|
||||||
|
tupleT,
|
||||||
|
optionalT,
|
||||||
|
uintT,
|
||||||
|
bufferT,
|
||||||
|
listT,
|
||||||
|
traitT,
|
||||||
|
responseSimpleT,
|
||||||
|
booleanT,
|
||||||
|
stringT,
|
||||||
|
principalT,
|
||||||
|
noneT
|
||||||
|
} from "../smartContractHelpers/codegenImport"
|
||||||
|
|
||||||
|
export const metaPegInEndpointV206Swap = defineContract({
|
||||||
|
"meta-peg-in-endpoint-v2-06-swap": {
|
||||||
|
'finalize-peg-in-cross-swap': {
|
||||||
|
input: [
|
||||||
|
{
|
||||||
|
name: 'commit-tx',
|
||||||
|
type: tupleT({ 'fee-idx': optionalT(uintT, ), 'output-idx': uintT, tx: bufferT }, )
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reveal-tx',
|
||||||
|
type: tupleT({ 'order-idx': uintT, tx: bufferT }, )
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reveal-block',
|
||||||
|
type: tupleT({ header: bufferT, height: uintT }, )
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reveal-proof',
|
||||||
|
type: tupleT({ hashes: listT(bufferT, ), 'tree-depth': uintT, 'tx-index': uintT }, )
|
||||||
|
},
|
||||||
|
{ name: 'routing-traits', type: listT(traitT, ) },
|
||||||
|
{ name: 'token-out-trait', type: traitT }
|
||||||
|
],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'finalize-peg-in-cross-swap-on-index': {
|
||||||
|
input: [
|
||||||
|
{
|
||||||
|
name: 'tx',
|
||||||
|
type: tupleT({
|
||||||
|
amt: uintT,
|
||||||
|
'bitcoin-tx': bufferT,
|
||||||
|
decimals: uintT,
|
||||||
|
from: bufferT,
|
||||||
|
'from-bal': uintT,
|
||||||
|
output: uintT,
|
||||||
|
tick: stringT,
|
||||||
|
to: bufferT,
|
||||||
|
'to-bal': uintT
|
||||||
|
}, )
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'block',
|
||||||
|
type: tupleT({ header: bufferT, height: uintT }, )
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'proof',
|
||||||
|
type: tupleT({ hashes: listT(bufferT, ), 'tree-depth': uintT, 'tx-index': uintT }, )
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'signature-packs',
|
||||||
|
type: listT(tupleT({ signature: bufferT, signer: principalT, 'tx-hash': bufferT }, ), )
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reveal-tx',
|
||||||
|
type: tupleT({ 'order-idx': uintT, tx: bufferT }, )
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reveal-block',
|
||||||
|
type: tupleT({ header: bufferT, height: uintT }, )
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reveal-proof',
|
||||||
|
type: tupleT({ hashes: listT(bufferT, ), 'tree-depth': uintT, 'tx-index': uintT }, )
|
||||||
|
},
|
||||||
|
{ name: 'fee-idx', type: optionalT(uintT, ) },
|
||||||
|
{ name: 'routing-traits', type: listT(traitT, ) },
|
||||||
|
{ name: 'token-out-trait', type: traitT }
|
||||||
|
],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
pause: {
|
||||||
|
input: [ { name: 'new-paused', type: booleanT } ],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'set-btc-peg-out-fee': {
|
||||||
|
input: [ { name: 'fee', type: uintT } ],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'set-btc-peg-out-min-fee': {
|
||||||
|
input: [ { name: 'fee', type: uintT } ],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'set-fee-to-address': {
|
||||||
|
input: [ { name: 'new-fee-to-address', type: principalT } ],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'set-peg-in-fee': {
|
||||||
|
input: [ { name: 'fee', type: uintT } ],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'transfer-all-to': {
|
||||||
|
input: [
|
||||||
|
{ name: 'new-owner', type: principalT },
|
||||||
|
{ name: 'token-trait', type: traitT }
|
||||||
|
],
|
||||||
|
output: responseSimpleT(booleanT, ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'transfer-all-to-many': {
|
||||||
|
input: [
|
||||||
|
{ name: 'new-owner', type: principalT },
|
||||||
|
{ name: 'token-traits', type: listT(traitT, ) }
|
||||||
|
],
|
||||||
|
output: responseSimpleT(listT(responseSimpleT(booleanT, ), ), ),
|
||||||
|
mode: 'public'
|
||||||
|
},
|
||||||
|
'break-routing-id': {
|
||||||
|
input: [
|
||||||
|
{ name: 'token-in', type: principalT },
|
||||||
|
{ name: 'routing-ids', type: listT(uintT, ) }
|
||||||
|
],
|
||||||
|
output: responseSimpleT(tupleT({
|
||||||
|
'routing-factors': listT(uintT, ),
|
||||||
|
'routing-tokens': listT(principalT, )
|
||||||
|
}, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'create-order-cross-swap-or-fail': {
|
||||||
|
input: [
|
||||||
|
{
|
||||||
|
name: 'order',
|
||||||
|
type: tupleT({
|
||||||
|
'chain-id': optionalT(uintT, ),
|
||||||
|
from: bufferT,
|
||||||
|
'min-amount-out': optionalT(uintT, ),
|
||||||
|
routing: listT(uintT, ),
|
||||||
|
to: bufferT,
|
||||||
|
'token-out': principalT
|
||||||
|
}, )
|
||||||
|
}
|
||||||
|
],
|
||||||
|
output: responseSimpleT(bufferT, ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'decode-order-cross-swap-from-reveal-tx-or-fail': {
|
||||||
|
input: [
|
||||||
|
{ name: 'tx', type: bufferT },
|
||||||
|
{ name: 'order-idx', type: uintT }
|
||||||
|
],
|
||||||
|
output: responseSimpleT(tupleT({
|
||||||
|
'commit-txid': bufferT,
|
||||||
|
'order-details': tupleT({
|
||||||
|
'chain-id': optionalT(uintT, ),
|
||||||
|
from: bufferT,
|
||||||
|
'min-amount-out': optionalT(uintT, ),
|
||||||
|
routing: listT(uintT, ),
|
||||||
|
to: bufferT,
|
||||||
|
'token-out': principalT
|
||||||
|
}, )
|
||||||
|
}, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'decode-order-cross-swap-or-fail': {
|
||||||
|
input: [ { name: 'order-script', type: bufferT } ],
|
||||||
|
output: responseSimpleT(tupleT({
|
||||||
|
'chain-id': optionalT(uintT, ),
|
||||||
|
from: bufferT,
|
||||||
|
'min-amount-out': optionalT(uintT, ),
|
||||||
|
routing: listT(uintT, ),
|
||||||
|
to: bufferT,
|
||||||
|
'token-out': principalT
|
||||||
|
}, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'get-btc-peg-out-fee': { input: [], output: uintT, mode: 'readonly' },
|
||||||
|
'get-btc-peg-out-min-fee': { input: [], output: uintT, mode: 'readonly' },
|
||||||
|
'get-fee-to-address': { input: [], output: principalT, mode: 'readonly' },
|
||||||
|
'get-pair-details': {
|
||||||
|
input: [
|
||||||
|
{
|
||||||
|
name: 'pair',
|
||||||
|
type: tupleT({ 'chain-id': uintT, token: principalT }, )
|
||||||
|
}
|
||||||
|
],
|
||||||
|
output: optionalT(tupleT({
|
||||||
|
approved: booleanT,
|
||||||
|
'no-burn': booleanT,
|
||||||
|
'peg-in-fee': uintT,
|
||||||
|
'peg-in-paused': booleanT,
|
||||||
|
'peg-out-fee': uintT,
|
||||||
|
'peg-out-gas-fee': uintT,
|
||||||
|
'peg-out-paused': booleanT,
|
||||||
|
tick: stringT
|
||||||
|
}, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'get-pair-details-many': {
|
||||||
|
input: [
|
||||||
|
{
|
||||||
|
name: 'pairs',
|
||||||
|
type: listT(tupleT({ 'chain-id': uintT, token: principalT }, ), )
|
||||||
|
}
|
||||||
|
],
|
||||||
|
output: listT(optionalT(tupleT({
|
||||||
|
approved: booleanT,
|
||||||
|
'no-burn': booleanT,
|
||||||
|
'peg-in-fee': uintT,
|
||||||
|
'peg-in-paused': booleanT,
|
||||||
|
'peg-out-fee': uintT,
|
||||||
|
'peg-out-gas-fee': uintT,
|
||||||
|
'peg-out-paused': booleanT,
|
||||||
|
tick: stringT
|
||||||
|
}, ), ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'get-pair-details-or-fail': {
|
||||||
|
input: [
|
||||||
|
{
|
||||||
|
name: 'pair',
|
||||||
|
type: tupleT({ 'chain-id': uintT, token: principalT }, )
|
||||||
|
}
|
||||||
|
],
|
||||||
|
output: responseSimpleT(tupleT({
|
||||||
|
approved: booleanT,
|
||||||
|
'no-burn': booleanT,
|
||||||
|
'peg-in-fee': uintT,
|
||||||
|
'peg-in-paused': booleanT,
|
||||||
|
'peg-out-fee': uintT,
|
||||||
|
'peg-out-gas-fee': uintT,
|
||||||
|
'peg-out-paused': booleanT,
|
||||||
|
tick: stringT
|
||||||
|
}, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'get-peg-in-fee': { input: [], output: uintT, mode: 'readonly' },
|
||||||
|
'get-peg-in-sent-or-default': {
|
||||||
|
input: [
|
||||||
|
{ name: 'bitcoin-tx', type: bufferT },
|
||||||
|
{ name: 'output', type: uintT },
|
||||||
|
{ name: 'offset', type: uintT }
|
||||||
|
],
|
||||||
|
output: booleanT,
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'get-tick-to-pair-or-fail': {
|
||||||
|
input: [ { name: 'tick', type: stringT } ],
|
||||||
|
output: responseSimpleT(tupleT({ 'chain-id': uintT, token: principalT }, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'is-approved-pair': {
|
||||||
|
input: [
|
||||||
|
{
|
||||||
|
name: 'pair',
|
||||||
|
type: tupleT({ 'chain-id': uintT, token: principalT }, )
|
||||||
|
}
|
||||||
|
],
|
||||||
|
output: booleanT,
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'is-dao-or-extension': { input: [], output: responseSimpleT(booleanT, ), mode: 'readonly' },
|
||||||
|
'is-paused': { input: [], output: booleanT, mode: 'readonly' },
|
||||||
|
'is-peg-in-address-approved': {
|
||||||
|
input: [ { name: 'address', type: bufferT } ],
|
||||||
|
output: booleanT,
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'validate-tx-cross-swap': {
|
||||||
|
input: [
|
||||||
|
{
|
||||||
|
name: 'commit-tx',
|
||||||
|
type: tupleT({ 'fee-idx': optionalT(uintT, ), 'output-idx': uintT, tx: bufferT }, )
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reveal-tx',
|
||||||
|
type: tupleT({ 'order-idx': uintT, tx: bufferT }, )
|
||||||
|
},
|
||||||
|
{ name: 'routing-traits', type: listT(traitT, ) },
|
||||||
|
{ name: 'token-out-trait', type: traitT }
|
||||||
|
],
|
||||||
|
output: responseSimpleT(tupleT({
|
||||||
|
'amt-net': uintT,
|
||||||
|
fee: uintT,
|
||||||
|
'order-details': tupleT({
|
||||||
|
'chain-id': optionalT(uintT, ),
|
||||||
|
from: bufferT,
|
||||||
|
'min-amount-out': optionalT(uintT, ),
|
||||||
|
routing: listT(uintT, ),
|
||||||
|
to: bufferT,
|
||||||
|
'token-out': principalT
|
||||||
|
}, ),
|
||||||
|
'pair-details': tupleT({ 'chain-id': uintT, token: principalT }, ),
|
||||||
|
'routing-factors': listT(uintT, ),
|
||||||
|
'routing-tokens': listT(principalT, ),
|
||||||
|
'token-details': tupleT({
|
||||||
|
approved: booleanT,
|
||||||
|
'no-burn': booleanT,
|
||||||
|
'peg-in-fee': uintT,
|
||||||
|
'peg-in-paused': booleanT,
|
||||||
|
'peg-out-fee': uintT,
|
||||||
|
'peg-out-gas-fee': uintT,
|
||||||
|
'peg-out-paused': booleanT,
|
||||||
|
tick: stringT
|
||||||
|
}, ),
|
||||||
|
'tx-idxed': tupleT({ amt: uintT, from: bufferT, tick: stringT, to: bufferT }, )
|
||||||
|
}, ), ),
|
||||||
|
mode: 'readonly'
|
||||||
|
},
|
||||||
|
'btc-peg-out-fee': { input: noneT, output: uintT, mode: 'variable' },
|
||||||
|
'btc-peg-out-min-fee': { input: noneT, output: uintT, mode: 'variable' },
|
||||||
|
'fee-to-address': { input: noneT, output: principalT, mode: 'variable' },
|
||||||
|
paused: { input: noneT, output: booleanT, mode: 'variable' },
|
||||||
|
'peg-in-fee': { input: noneT, output: uintT, mode: 'variable' }
|
||||||
|
}
|
||||||
|
} as const)
|
||||||
|
|
||||||
|
|
||||||
@@ -1,17 +1,21 @@
|
|||||||
import { defineContract } from "../smartContractHelpers/codegenImport";
|
import { defineContract } from "../smartContractHelpers/codegenImport";
|
||||||
import { btcPegInEndpointV205 } from "./contract_xlink_btc-peg-in-endpoint-v2-05"
|
import { btcPegInEndpointV205 } from "./contract_xlink_btc-peg-in-endpoint-v2-05"
|
||||||
|
import { btcPegInEndpointV207Swap } from "./contract_xlink_btc-peg-in-endpoint-v2-07-swap"
|
||||||
import { btcPegOutEndpointV201 } from "./contract_xlink_btc-peg-out-endpoint-v2-01"
|
import { btcPegOutEndpointV201 } from "./contract_xlink_btc-peg-out-endpoint-v2-01"
|
||||||
import { crossPegInEndpointV204 } from "./contract_xlink_cross-peg-in-endpoint-v2-04"
|
import { crossPegInEndpointV204 } from "./contract_xlink_cross-peg-in-endpoint-v2-04"
|
||||||
import { crossPegOutEndpointV201 } from "./contract_xlink_cross-peg-out-endpoint-v2-01"
|
import { crossPegOutEndpointV201 } from "./contract_xlink_cross-peg-out-endpoint-v2-01"
|
||||||
import { metaPegInEndpointV204 } from "./contract_xlink_meta-peg-in-endpoint-v2-04"
|
import { metaPegInEndpointV204 } from "./contract_xlink_meta-peg-in-endpoint-v2-04"
|
||||||
|
import { metaPegInEndpointV206Swap } from "./contract_xlink_meta-peg-in-endpoint-v2-06-swap"
|
||||||
import { metaPegOutEndpointV204 } from "./contract_xlink_meta-peg-out-endpoint-v2-04"
|
import { metaPegOutEndpointV204 } from "./contract_xlink_meta-peg-out-endpoint-v2-04"
|
||||||
|
|
||||||
export const xlinkContracts = defineContract({
|
export const xlinkContracts = defineContract({
|
||||||
...btcPegInEndpointV205,
|
...btcPegInEndpointV205,
|
||||||
|
...btcPegInEndpointV207Swap,
|
||||||
...btcPegOutEndpointV201,
|
...btcPegOutEndpointV201,
|
||||||
...crossPegInEndpointV204,
|
...crossPegInEndpointV204,
|
||||||
...crossPegOutEndpointV201,
|
...crossPegOutEndpointV201,
|
||||||
...metaPegInEndpointV204,
|
...metaPegInEndpointV204,
|
||||||
|
...metaPegInEndpointV206Swap,
|
||||||
...metaPegOutEndpointV204
|
...metaPegOutEndpointV204
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -41,9 +41,9 @@
|
|||||||
"gen": "pnpm run gen:stacksContract",
|
"gen": "pnpm run gen:stacksContract",
|
||||||
"docs": "typedoc",
|
"docs": "typedoc",
|
||||||
"docs:watch": "typedoc --watch",
|
"docs:watch": "typedoc --watch",
|
||||||
"build": "pnpm run gen && rm -rf lib && tsup-node --sourcemap --dts -d lib --format cjs,esm src",
|
"build": "pnpm run gen && rm -rf lib && tsup-node src",
|
||||||
"prepare": "pnpm run build",
|
"prepare": "pnpm run build",
|
||||||
"test": "vitest --exclude lib"
|
"test": "vitest --exclude lib --run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@c4/btc-utils": "^0.3.1",
|
"@c4/btc-utils": "^0.3.1",
|
||||||
|
|||||||
@@ -1,32 +1,52 @@
|
|||||||
import { generateContracts } from "clarity-codegen/lib/generate"
|
import { generateContracts } from "clarity-codegen/lib/generate"
|
||||||
import * as path from "node:path"
|
import * as path from "node:path"
|
||||||
import { STACKS_MAINNET } from "../src/config"
|
|
||||||
import {
|
import {
|
||||||
stxContractDeployers,
|
contractNameOverrides,
|
||||||
|
envName,
|
||||||
|
STACKS_MAINNET,
|
||||||
|
STACKS_TESTNET,
|
||||||
|
} from "../src/config"
|
||||||
|
import {
|
||||||
|
StacksContractName,
|
||||||
|
stxContractAddresses,
|
||||||
xlinkContractsMultisigMainnet,
|
xlinkContractsMultisigMainnet,
|
||||||
|
xlinkContractsMultisigTestnet,
|
||||||
} from "../src/stacksUtils/stxContractAddresses"
|
} from "../src/stacksUtils/stxContractAddresses"
|
||||||
import { KnownChainId } from "../src/utils/types/knownIds"
|
import { KnownChainId } from "../src/utils/types/knownIds"
|
||||||
;(async function main(): Promise<void> {
|
;(async function main(): Promise<void> {
|
||||||
|
const stacksChainId =
|
||||||
|
envName === "prod"
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet
|
||||||
|
const fallbackDeployerAddress =
|
||||||
|
envName === "prod"
|
||||||
|
? xlinkContractsMultisigMainnet
|
||||||
|
: xlinkContractsMultisigTestnet
|
||||||
|
const fallbackStacksNetwork =
|
||||||
|
envName === "prod" ? STACKS_MAINNET : STACKS_TESTNET
|
||||||
|
|
||||||
await generateContracts(
|
await generateContracts(
|
||||||
process.env.STACKS_CORE_API_URL ?? STACKS_MAINNET.coreApiUrl,
|
process.env.STACKS_CORE_API_URL ?? fallbackStacksNetwork.coreApiUrl,
|
||||||
contractName => {
|
contractName => {
|
||||||
return (
|
return (
|
||||||
stxContractDeployers[
|
stxContractAddresses[contractName as StacksContractName]?.[
|
||||||
contractName as keyof typeof stxContractDeployers
|
stacksChainId
|
||||||
]?.[KnownChainId.Stacks.Mainnet]?.deployerAddress ??
|
]?.deployerAddress ?? fallbackDeployerAddress
|
||||||
xlinkContractsMultisigMainnet
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
"btc-peg-in-endpoint-v2-05",
|
"btc-peg-in-endpoint-v2-05",
|
||||||
|
"btc-peg-in-endpoint-v2-07-swap",
|
||||||
"btc-peg-out-endpoint-v2-01",
|
"btc-peg-out-endpoint-v2-01",
|
||||||
"cross-peg-in-endpoint-v2-04",
|
"cross-peg-in-endpoint-v2-04",
|
||||||
"cross-peg-out-endpoint-v2-01",
|
"cross-peg-out-endpoint-v2-01",
|
||||||
"meta-peg-in-endpoint-v2-04",
|
"meta-peg-in-endpoint-v2-04",
|
||||||
|
"meta-peg-in-endpoint-v2-06-swap",
|
||||||
"meta-peg-out-endpoint-v2-04",
|
"meta-peg-out-endpoint-v2-04",
|
||||||
],
|
],
|
||||||
path.resolve(__dirname, "../generated/smartContract/"),
|
path.resolve(__dirname, "../generated/smartContract/"),
|
||||||
"xlink",
|
"xlink",
|
||||||
"../smartContractHelpers/codegenImport",
|
"../smartContractHelpers/codegenImport",
|
||||||
|
contractNameOverrides,
|
||||||
)
|
)
|
||||||
})().catch(console.error)
|
})().catch(console.error)
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ async function print(matchers: { chain: string[] }): Promise<void> {
|
|||||||
backendAPI: {
|
backendAPI: {
|
||||||
runtimeEnv: "prod",
|
runtimeEnv: "prod",
|
||||||
},
|
},
|
||||||
|
stacks: {},
|
||||||
|
btc: {},
|
||||||
brc20: {},
|
brc20: {},
|
||||||
runes: {},
|
runes: {},
|
||||||
evm: {
|
evm: {
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { Client } from "viem"
|
import { Client } from "viem"
|
||||||
import { getBTCPegInAddress } from "./bitcoinUtils/btcAddresses"
|
import { getBTCPegInAddress } from "./bitcoinUtils/btcAddresses"
|
||||||
import { nativeCurrencyAddress } from "./evmUtils/addressHelpers"
|
import { nativeCurrencyAddress } from "./evmUtils/addressHelpers"
|
||||||
import { defaultEvmClients } from "./evmUtils/evmClients"
|
import {
|
||||||
|
defaultEvmClients,
|
||||||
|
evmChainIdFromKnownChainId,
|
||||||
|
evmChainIdToKnownChainId,
|
||||||
|
} from "./evmUtils/evmClients"
|
||||||
import {
|
import {
|
||||||
getEVMContractCallInfo,
|
getEVMContractCallInfo,
|
||||||
getEVMToken,
|
getEVMToken,
|
||||||
@@ -124,6 +128,15 @@ export interface XLinkSDKOptions {
|
|||||||
backendAPI?: {
|
backendAPI?: {
|
||||||
runtimeEnv?: "prod" | "dev"
|
runtimeEnv?: "prod" | "dev"
|
||||||
}
|
}
|
||||||
|
btc?: {
|
||||||
|
ignoreValidateResult?: boolean
|
||||||
|
}
|
||||||
|
brc20?: {
|
||||||
|
ignoreValidateResult?: boolean
|
||||||
|
}
|
||||||
|
runes?: {
|
||||||
|
ignoreValidateResult?: boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
evm?: {
|
evm?: {
|
||||||
/**
|
/**
|
||||||
@@ -160,11 +173,22 @@ export class XLinkSDK {
|
|||||||
...options.__experimental?.backendAPI,
|
...options.__experimental?.backendAPI,
|
||||||
runtimeEnv: options.__experimental?.backendAPI?.runtimeEnv ?? "prod",
|
runtimeEnv: options.__experimental?.backendAPI?.runtimeEnv ?? "prod",
|
||||||
},
|
},
|
||||||
|
stacks: {
|
||||||
|
tokensCache: new Map(),
|
||||||
|
},
|
||||||
|
btc: {
|
||||||
|
ignoreValidateResult:
|
||||||
|
options.__experimental?.btc?.ignoreValidateResult ?? false,
|
||||||
|
},
|
||||||
brc20: {
|
brc20: {
|
||||||
routesConfigCache: new Map(),
|
routesConfigCache: new Map(),
|
||||||
|
ignoreValidateResult:
|
||||||
|
options.__experimental?.brc20?.ignoreValidateResult ?? false,
|
||||||
},
|
},
|
||||||
runes: {
|
runes: {
|
||||||
routesConfigCache: new Map(),
|
routesConfigCache: new Map(),
|
||||||
|
ignoreValidateResult:
|
||||||
|
options.__experimental?.runes?.ignoreValidateResult ?? false,
|
||||||
},
|
},
|
||||||
evm: {
|
evm: {
|
||||||
onChainConfigCache: cacheEVMOnChainConfig ? new Map() : undefined,
|
onChainConfigCache: cacheEVMOnChainConfig ? new Map() : undefined,
|
||||||
@@ -218,8 +242,18 @@ export class XLinkSDK {
|
|||||||
return checkingResult.some(r => r)
|
return checkingResult.some(r => r)
|
||||||
}
|
}
|
||||||
|
|
||||||
stacksAddressFromStacksToken = stacksAddressFromStacksToken
|
stacksAddressFromStacksToken(
|
||||||
stacksAddressToStacksToken = stacksAddressToStacksToken
|
chain: ChainId,
|
||||||
|
token: KnownTokenId.StacksToken,
|
||||||
|
): Promise<undefined | StacksContractAddress> {
|
||||||
|
return stacksAddressFromStacksToken(this.sdkContext, chain, token)
|
||||||
|
}
|
||||||
|
stacksAddressToStacksToken(
|
||||||
|
chain: ChainId,
|
||||||
|
address: StacksContractAddress,
|
||||||
|
): Promise<undefined | KnownTokenId.StacksToken> {
|
||||||
|
return stacksAddressToStacksToken(this.sdkContext, chain, address)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function provides detailed information about token transfers from the Stacks network to other supported
|
* This function provides detailed information about token transfers from the Stacks network to other supported
|
||||||
@@ -296,6 +330,18 @@ export class XLinkSDK {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async evmChainIdFromKnownChainId(
|
||||||
|
chain: KnownChainId.EVMChain,
|
||||||
|
): Promise<undefined | bigint> {
|
||||||
|
return evmChainIdFromKnownChainId(chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
async evmChainIdToKnownChainId(
|
||||||
|
chainId: bigint,
|
||||||
|
): Promise<undefined | KnownChainId.EVMChain> {
|
||||||
|
return evmChainIdToKnownChainId(chainId)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function retrieves the contract address of a specific token on a given EVM-compatible blockchain.
|
* This function retrieves the contract address of a specific token on a given EVM-compatible blockchain.
|
||||||
* @param chain - The ID of the EVM-compatible blockchain where the token contract is deployed.
|
* @param chain - The ID of the EVM-compatible blockchain where the token contract is deployed.
|
||||||
@@ -582,11 +628,12 @@ export class XLinkSDK {
|
|||||||
* or `undefined` if the chain is not a Stacks chain or if the contract address cannot be retrieved.
|
* or `undefined` if the chain is not a Stacks chain or if the contract address cannot be retrieved.
|
||||||
*/
|
*/
|
||||||
async function stacksAddressFromStacksToken(
|
async function stacksAddressFromStacksToken(
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
chain: ChainId,
|
chain: ChainId,
|
||||||
token: KnownTokenId.StacksToken,
|
token: KnownTokenId.StacksToken,
|
||||||
): Promise<undefined | StacksContractAddress> {
|
): Promise<undefined | StacksContractAddress> {
|
||||||
if (!KnownChainId.isStacksChain(chain)) return
|
if (!KnownChainId.isStacksChain(chain)) return
|
||||||
const info = await getStacksTokenContractInfo(chain, token)
|
const info = await getStacksTokenContractInfo(sdkContext, chain, token)
|
||||||
if (info == null) return
|
if (info == null) return
|
||||||
return {
|
return {
|
||||||
deployerAddress: info.deployerAddress,
|
deployerAddress: info.deployerAddress,
|
||||||
@@ -604,11 +651,12 @@ async function stacksAddressFromStacksToken(
|
|||||||
* cannot be found.
|
* cannot be found.
|
||||||
*/
|
*/
|
||||||
async function stacksAddressToStacksToken(
|
async function stacksAddressToStacksToken(
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
chain: ChainId,
|
chain: ChainId,
|
||||||
address: StacksContractAddress,
|
address: StacksContractAddress,
|
||||||
): Promise<undefined | KnownTokenId.StacksToken> {
|
): Promise<undefined | KnownTokenId.StacksToken> {
|
||||||
if (!KnownChainId.isStacksChain(chain)) return
|
if (!KnownChainId.isStacksChain(chain)) return
|
||||||
return getStacksToken(chain, address)
|
return getStacksToken(sdkContext, chain, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function brc20TickFromBRC20Token(
|
async function brc20TickFromBRC20Token(
|
||||||
@@ -627,7 +675,8 @@ async function brc20TickToBRC20Token(
|
|||||||
): Promise<undefined | KnownTokenId.BRC20Token> {
|
): Promise<undefined | KnownTokenId.BRC20Token> {
|
||||||
if (!KnownChainId.isBRC20Chain(chain)) return
|
if (!KnownChainId.isBRC20Chain(chain)) return
|
||||||
const routes = await getBRC20SupportedRoutes(sdkContext, chain)
|
const routes = await getBRC20SupportedRoutes(sdkContext, chain)
|
||||||
return routes.find(r => r.brc20Tick === tick)?.brc20Token
|
return routes.find(r => r.brc20Tick.toLowerCase() === tick.toLowerCase())
|
||||||
|
?.brc20Token
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runesIdFromRunesToken(
|
async function runesIdFromRunesToken(
|
||||||
|
|||||||
@@ -15,9 +15,17 @@ export async function createBitcoinPegInRecipients(
|
|||||||
sdkContext: Pick<SDKGlobalContext, "backendAPI">,
|
sdkContext: Pick<SDKGlobalContext, "backendAPI">,
|
||||||
info: {
|
info: {
|
||||||
fromChain: KnownChainId.BitcoinChain
|
fromChain: KnownChainId.BitcoinChain
|
||||||
toChain: KnownChainId.StacksChain | KnownChainId.EVMChain
|
|
||||||
fromToken: KnownTokenId.BitcoinToken
|
fromToken: KnownTokenId.BitcoinToken
|
||||||
toToken: KnownTokenId.StacksToken | KnownTokenId.EVMToken
|
toChain:
|
||||||
|
| KnownChainId.StacksChain
|
||||||
|
| KnownChainId.EVMChain
|
||||||
|
| KnownChainId.BRC20Chain
|
||||||
|
| KnownChainId.RunesChain
|
||||||
|
toToken:
|
||||||
|
| KnownTokenId.StacksToken
|
||||||
|
| KnownTokenId.EVMToken
|
||||||
|
| KnownTokenId.BRC20Token
|
||||||
|
| KnownTokenId.RunesToken
|
||||||
fromAddress: {
|
fromAddress: {
|
||||||
address: string
|
address: string
|
||||||
scriptPubKey: Uint8Array
|
scriptPubKey: Uint8Array
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
import { callReadOnlyFunction } from "@stacks/transactions"
|
import { evmTokenFromCorrespondingStacksToken } from "../evmUtils/peggingHelpers"
|
||||||
import { CallReadOnlyFunctionFn } from "clarity-codegen"
|
|
||||||
import { fromCorrespondingStacksToken } from "../evmUtils/peggingHelpers"
|
|
||||||
import { getEVMTokenContractInfo } from "../evmUtils/xlinkContractHelpers"
|
import { getEVMTokenContractInfo } from "../evmUtils/xlinkContractHelpers"
|
||||||
import { stxTokenContractAddresses } from "../stacksUtils/stxContractAddresses"
|
import {
|
||||||
|
getBRC20SupportedRoutes,
|
||||||
|
getRunesSupportedRoutes,
|
||||||
|
} from "../metaUtils/xlinkContractHelpers"
|
||||||
|
import {
|
||||||
|
StacksContractName,
|
||||||
|
stxTokenContractAddresses,
|
||||||
|
} from "../stacksUtils/stxContractAddresses"
|
||||||
import {
|
import {
|
||||||
executeReadonlyCallXLINK,
|
executeReadonlyCallXLINK,
|
||||||
getStacksContractCallInfo,
|
getStacksContractCallInfo,
|
||||||
@@ -13,53 +18,66 @@ import {
|
|||||||
IsSupportedFn,
|
IsSupportedFn,
|
||||||
KnownRoute_FromBitcoin_ToStacks,
|
KnownRoute_FromBitcoin_ToStacks,
|
||||||
KnownRoute_FromStacks_ToBitcoin,
|
KnownRoute_FromStacks_ToBitcoin,
|
||||||
|
KnownRoute_ToStacks,
|
||||||
} from "../utils/buildSupportedRoutes"
|
} from "../utils/buildSupportedRoutes"
|
||||||
import { props } from "../utils/promiseHelpers"
|
import { props } from "../utils/promiseHelpers"
|
||||||
import { checkNever } from "../utils/typeHelpers"
|
import {
|
||||||
import { TransferProphet } from "../utils/types/TransferProphet"
|
getFinalStepStacksTokenAddress,
|
||||||
|
SwapRoute,
|
||||||
|
} from "../utils/SwapRouteHelpers"
|
||||||
|
import { assertExclude, checkNever } from "../utils/typeHelpers"
|
||||||
import {
|
import {
|
||||||
_allNoLongerSupportedEVMChains,
|
_allNoLongerSupportedEVMChains,
|
||||||
KnownChainId,
|
KnownChainId,
|
||||||
KnownTokenId,
|
KnownTokenId,
|
||||||
} from "../utils/types/knownIds"
|
} from "../utils/types/knownIds"
|
||||||
|
import { TransferProphet } from "../utils/types/TransferProphet"
|
||||||
import { getBTCPegInAddress } from "./btcAddresses"
|
import { getBTCPegInAddress } from "./btcAddresses"
|
||||||
|
|
||||||
export const getBtc2StacksFeeInfo = async (
|
export const getBtc2StacksFeeInfo = async (
|
||||||
route: KnownRoute_FromBitcoin_ToStacks,
|
route: KnownRoute_FromBitcoin_ToStacks,
|
||||||
|
options: {
|
||||||
|
swapRoute: null | SwapRoute
|
||||||
|
},
|
||||||
): Promise<undefined | TransferProphet> => {
|
): Promise<undefined | TransferProphet> => {
|
||||||
const stacksContractCallInfo = getStacksContractCallInfo(
|
const stacksBaseContractCallInfo = getStacksContractCallInfo(
|
||||||
route.toChain,
|
route.toChain,
|
||||||
"btc-peg-in-endpoint-v2-05",
|
StacksContractName.BTCPegInEndpoint,
|
||||||
)
|
)
|
||||||
if (stacksContractCallInfo == null) return
|
const stacksSwapContractCallInfo = getStacksContractCallInfo(
|
||||||
|
route.toChain,
|
||||||
const executeOptions = {
|
StacksContractName.BTCPegInEndpointSwap,
|
||||||
deployerAddress: stacksContractCallInfo.deployerAddress,
|
)
|
||||||
callReadOnlyFunction: (callOptions =>
|
if (
|
||||||
callReadOnlyFunction({
|
stacksBaseContractCallInfo == null ||
|
||||||
...callOptions,
|
stacksSwapContractCallInfo == null
|
||||||
network: stacksContractCallInfo.network,
|
) {
|
||||||
})) satisfies CallReadOnlyFunctionFn,
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const contractCallInfo =
|
||||||
|
options.swapRoute == null
|
||||||
|
? stacksBaseContractCallInfo
|
||||||
|
: stacksSwapContractCallInfo
|
||||||
|
|
||||||
const resp = await props({
|
const resp = await props({
|
||||||
isPaused: executeReadonlyCallXLINK(
|
isPaused: executeReadonlyCallXLINK(
|
||||||
stacksContractCallInfo.contractName,
|
contractCallInfo.contractName,
|
||||||
"is-peg-in-paused",
|
"is-peg-in-paused",
|
||||||
{},
|
{},
|
||||||
executeOptions,
|
contractCallInfo.executeOptions,
|
||||||
),
|
),
|
||||||
feeRate: executeReadonlyCallXLINK(
|
feeRate: executeReadonlyCallXLINK(
|
||||||
stacksContractCallInfo.contractName,
|
contractCallInfo.contractName,
|
||||||
"get-peg-in-fee",
|
"get-peg-in-fee",
|
||||||
{},
|
{},
|
||||||
executeOptions,
|
contractCallInfo.executeOptions,
|
||||||
).then(numberFromStacksContractNumber),
|
).then(numberFromStacksContractNumber),
|
||||||
minFeeAmount: executeReadonlyCallXLINK(
|
minFeeAmount: executeReadonlyCallXLINK(
|
||||||
stacksContractCallInfo.contractName,
|
contractCallInfo.contractName,
|
||||||
"get-peg-in-min-fee",
|
"get-peg-in-min-fee",
|
||||||
{},
|
{},
|
||||||
executeOptions,
|
contractCallInfo.executeOptions,
|
||||||
).then(numberFromStacksContractNumber),
|
).then(numberFromStacksContractNumber),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -83,41 +101,103 @@ export const getBtc2StacksFeeInfo = async (
|
|||||||
|
|
||||||
export const getStacks2BtcFeeInfo = async (
|
export const getStacks2BtcFeeInfo = async (
|
||||||
route: KnownRoute_FromStacks_ToBitcoin,
|
route: KnownRoute_FromStacks_ToBitcoin,
|
||||||
|
options: {
|
||||||
|
swappedFromRoute: null | KnownRoute_ToStacks
|
||||||
|
},
|
||||||
): Promise<undefined | TransferProphet> => {
|
): Promise<undefined | TransferProphet> => {
|
||||||
const stacksContractCallInfo = getStacksContractCallInfo(
|
const stacksContractCallInfo = getStacksContractCallInfo(
|
||||||
route.fromChain,
|
route.fromChain,
|
||||||
"btc-peg-out-endpoint-v2-01",
|
StacksContractName.BTCPegOutEndpoint,
|
||||||
)
|
)
|
||||||
if (stacksContractCallInfo == null) return
|
const btcPegInSwapContractCallInfo = getStacksContractCallInfo(
|
||||||
|
route.fromChain,
|
||||||
|
StacksContractName.BTCPegInEndpointSwap,
|
||||||
|
)
|
||||||
|
const metaPegInSwapContractCallInfo = getStacksContractCallInfo(
|
||||||
|
route.fromChain,
|
||||||
|
StacksContractName.MetaPegInEndpointSwap,
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
stacksContractCallInfo == null ||
|
||||||
|
btcPegInSwapContractCallInfo == null ||
|
||||||
|
metaPegInSwapContractCallInfo == null
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const executeOptions = {
|
let feeInfo:
|
||||||
deployerAddress: stacksContractCallInfo.deployerAddress,
|
| undefined
|
||||||
callReadOnlyFunction: (callOptions =>
|
| {
|
||||||
callReadOnlyFunction({
|
feeRate: Promise<BigNumber>
|
||||||
...callOptions,
|
minFeeAmount: Promise<BigNumber>
|
||||||
network: stacksContractCallInfo.network,
|
}
|
||||||
})) satisfies CallReadOnlyFunctionFn,
|
if (options.swappedFromRoute != null) {
|
||||||
|
if (KnownChainId.isBitcoinChain(options.swappedFromRoute.fromChain)) {
|
||||||
|
feeInfo = {
|
||||||
|
feeRate: executeReadonlyCallXLINK(
|
||||||
|
btcPegInSwapContractCallInfo.contractName,
|
||||||
|
"get-btc-peg-out-fee",
|
||||||
|
{},
|
||||||
|
btcPegInSwapContractCallInfo.executeOptions,
|
||||||
|
).then(numberFromStacksContractNumber),
|
||||||
|
minFeeAmount: executeReadonlyCallXLINK(
|
||||||
|
btcPegInSwapContractCallInfo.contractName,
|
||||||
|
"get-btc-peg-out-min-fee",
|
||||||
|
{},
|
||||||
|
btcPegInSwapContractCallInfo.executeOptions,
|
||||||
|
).then(numberFromStacksContractNumber),
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
KnownChainId.isBRC20Chain(options.swappedFromRoute.fromChain) ||
|
||||||
|
KnownChainId.isRunesChain(options.swappedFromRoute.fromChain)
|
||||||
|
) {
|
||||||
|
feeInfo = {
|
||||||
|
feeRate: executeReadonlyCallXLINK(
|
||||||
|
metaPegInSwapContractCallInfo.contractName,
|
||||||
|
"get-btc-peg-out-fee",
|
||||||
|
{},
|
||||||
|
metaPegInSwapContractCallInfo.executeOptions,
|
||||||
|
).then(numberFromStacksContractNumber),
|
||||||
|
minFeeAmount: executeReadonlyCallXLINK(
|
||||||
|
metaPegInSwapContractCallInfo.contractName,
|
||||||
|
"get-btc-peg-out-min-fee",
|
||||||
|
{},
|
||||||
|
metaPegInSwapContractCallInfo.executeOptions,
|
||||||
|
).then(numberFromStacksContractNumber),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assertExclude(
|
||||||
|
options.swappedFromRoute.fromChain,
|
||||||
|
assertExclude.i<KnownChainId.EVMChain>(),
|
||||||
|
)
|
||||||
|
checkNever(options.swappedFromRoute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (feeInfo == null) {
|
||||||
|
feeInfo = {
|
||||||
|
feeRate: executeReadonlyCallXLINK(
|
||||||
|
stacksContractCallInfo.contractName,
|
||||||
|
"get-peg-out-fee",
|
||||||
|
{},
|
||||||
|
stacksContractCallInfo.executeOptions,
|
||||||
|
).then(numberFromStacksContractNumber),
|
||||||
|
minFeeAmount: executeReadonlyCallXLINK(
|
||||||
|
stacksContractCallInfo.contractName,
|
||||||
|
"get-peg-out-min-fee",
|
||||||
|
{},
|
||||||
|
stacksContractCallInfo.executeOptions,
|
||||||
|
).then(numberFromStacksContractNumber),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const resp = await props({
|
const resp = await props({
|
||||||
|
...feeInfo,
|
||||||
isPaused: executeReadonlyCallXLINK(
|
isPaused: executeReadonlyCallXLINK(
|
||||||
stacksContractCallInfo.contractName,
|
stacksContractCallInfo.contractName,
|
||||||
"is-peg-out-paused",
|
"is-peg-out-paused",
|
||||||
{},
|
{},
|
||||||
executeOptions,
|
stacksContractCallInfo.executeOptions,
|
||||||
),
|
),
|
||||||
feeRate: executeReadonlyCallXLINK(
|
|
||||||
stacksContractCallInfo.contractName,
|
|
||||||
"get-peg-out-fee",
|
|
||||||
{},
|
|
||||||
executeOptions,
|
|
||||||
).then(numberFromStacksContractNumber),
|
|
||||||
minFeeAmount: executeReadonlyCallXLINK(
|
|
||||||
stacksContractCallInfo.contractName,
|
|
||||||
"get-peg-out-min-fee",
|
|
||||||
{},
|
|
||||||
executeOptions,
|
|
||||||
).then(numberFromStacksContractNumber),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -172,21 +252,25 @@ export const isSupportedBitcoinRoute: IsSupportedFn = async (ctx, route) => {
|
|||||||
if (KnownChainId.isBitcoinChain(toChain)) {
|
if (KnownChainId.isBitcoinChain(toChain)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (KnownChainId.isRunesChain(toChain)) {
|
|
||||||
return false
|
const finalStepStacksToken =
|
||||||
}
|
route.swapRoute == null
|
||||||
if (KnownChainId.isBRC20Chain(toChain)) {
|
? KnownTokenId.Stacks.aBTC
|
||||||
return false
|
: await getFinalStepStacksTokenAddress(ctx, {
|
||||||
}
|
swap: route.swapRoute,
|
||||||
|
stacksChain:
|
||||||
|
fromChain === KnownChainId.Bitcoin.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
})
|
||||||
|
|
||||||
if (KnownChainId.isStacksChain(toChain)) {
|
if (KnownChainId.isStacksChain(toChain)) {
|
||||||
if (!KnownTokenId.isStacksToken(toToken)) return false
|
if (!KnownTokenId.isStacksToken(toToken)) return false
|
||||||
|
|
||||||
return (
|
if (fromToken !== KnownTokenId.Bitcoin.BTC) return false
|
||||||
fromToken === KnownTokenId.Bitcoin.BTC &&
|
if (stxTokenContractAddresses[toToken]?.[toChain] == null) return false
|
||||||
toToken === KnownTokenId.Stacks.aBTC &&
|
|
||||||
stxTokenContractAddresses[toToken]?.[toChain] != null
|
return toToken === finalStepStacksToken
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (KnownChainId.isEVMChain(toChain)) {
|
if (KnownChainId.isEVMChain(toChain)) {
|
||||||
@@ -195,11 +279,30 @@ export const isSupportedBitcoinRoute: IsSupportedFn = async (ctx, route) => {
|
|||||||
const info = await getEVMTokenContractInfo(ctx, toChain, toToken)
|
const info = await getEVMTokenContractInfo(ctx, toChain, toToken)
|
||||||
if (info == null) return false
|
if (info == null) return false
|
||||||
|
|
||||||
const toEVMTokens = await fromCorrespondingStacksToken(
|
if (finalStepStacksToken == null) return false
|
||||||
|
|
||||||
|
return evmTokenFromCorrespondingStacksToken(
|
||||||
toChain,
|
toChain,
|
||||||
KnownTokenId.Stacks.aBTC,
|
finalStepStacksToken,
|
||||||
|
).then(toEVMTokens => toEVMTokens.includes(toToken))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (KnownChainId.isRunesChain(toChain)) {
|
||||||
|
const routes = await getRunesSupportedRoutes(ctx, toChain)
|
||||||
|
return routes.some(
|
||||||
|
route =>
|
||||||
|
route.runesToken === toToken &&
|
||||||
|
route.stacksToken === finalStepStacksToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (KnownChainId.isBRC20Chain(toChain)) {
|
||||||
|
const routes = await getBRC20SupportedRoutes(ctx, toChain)
|
||||||
|
return routes.some(
|
||||||
|
route =>
|
||||||
|
route.brc20Token === toToken &&
|
||||||
|
route.stacksToken === finalStepStacksToken,
|
||||||
)
|
)
|
||||||
return toEVMTokens.includes(toToken as any)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkNever(toChain)
|
checkNever(toChain)
|
||||||
|
|||||||
@@ -8,3 +8,17 @@ export const STACKS_MAINNET = new StacksMainnet({
|
|||||||
export const STACKS_TESTNET = new StacksMocknet({
|
export const STACKS_TESTNET = new StacksMocknet({
|
||||||
url: "https://nakamoto-dev-api.alexlab.co",
|
url: "https://nakamoto-dev-api.alexlab.co",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const envName = process.env.ENV_NAME === "dev" ? "dev" : "prod"
|
||||||
|
export const contractNameOverrides: Record<string, string> =
|
||||||
|
envName === "prod"
|
||||||
|
? {
|
||||||
|
"btc-peg-in-endpoint-v2-07-swap": "btc-peg-in-v2-07-swap",
|
||||||
|
"meta-peg-in-endpoint-v2-06-swap": "meta-peg-in-v2-06-swap",
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
"btc-peg-in-endpoint-v2-07-swap": "btc-peg-in-v2-07-swap-01",
|
||||||
|
"meta-peg-in-endpoint-v2-06-swap": "meta-peg-in-v2-06-swap-01",
|
||||||
|
"meta-peg-out-endpoint-v2-04": "meta-peg-out-endpoint-v2-04-da",
|
||||||
|
"token-wvliabtc": "token-wvliabtc-da",
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,129 +25,146 @@ import {
|
|||||||
xLayer,
|
xLayer,
|
||||||
} from "./evmChainInfos"
|
} from "./evmChainInfos"
|
||||||
import { EVMChain } from "./evmContractAddresses"
|
import { EVMChain } from "./evmContractAddresses"
|
||||||
|
import { KnownChainId } from "../utils/types/knownIds"
|
||||||
|
import { entries } from "../utils/objectHelper"
|
||||||
|
|
||||||
export const defaultEvmClients: Record<
|
export const defaultEvmClients: Partial<Record<KnownChainId.EVMChain, Client>> =
|
||||||
Exclude<EVMChain, typeof EVMChain.BitboyTestnet>,
|
{
|
||||||
Client
|
[EVMChain.Ethereum]: createClient({
|
||||||
> = {
|
chain: mainnet,
|
||||||
[EVMChain.Ethereum]: createClient({
|
transport: http(),
|
||||||
chain: mainnet,
|
batch: { multicall: true },
|
||||||
transport: http(),
|
}),
|
||||||
batch: { multicall: true },
|
[EVMChain.Sepolia]: createClient({
|
||||||
}),
|
chain: sepolia,
|
||||||
[EVMChain.Sepolia]: createClient({
|
transport: http(),
|
||||||
chain: sepolia,
|
batch: { multicall: true },
|
||||||
transport: http(),
|
}),
|
||||||
batch: { multicall: true },
|
|
||||||
}),
|
|
||||||
|
|
||||||
[EVMChain.BSC]: createClient({
|
[EVMChain.BSC]: createClient({
|
||||||
chain: bsc,
|
chain: bsc,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
[EVMChain.BSCTestnet]: createClient({
|
[EVMChain.BSCTestnet]: createClient({
|
||||||
chain: bscTestnet,
|
chain: bscTestnet,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.CoreDAO]: createClient({
|
[EVMChain.CoreDAO]: createClient({
|
||||||
chain: coreDao,
|
chain: coreDao,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
[EVMChain.CoreDAOTestnet]: createClient({
|
[EVMChain.CoreDAOTestnet]: createClient({
|
||||||
chain: coreDaoTestnet,
|
chain: coreDaoTestnet,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.Bsquared]: createClient({
|
[EVMChain.Bsquared]: createClient({
|
||||||
chain: bsquared,
|
chain: bsquared,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.BOB]: createClient({
|
[EVMChain.BOB]: createClient({
|
||||||
chain: bob,
|
chain: bob,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.Bitlayer]: createClient({
|
[EVMChain.Bitlayer]: createClient({
|
||||||
chain: bitlayer,
|
chain: bitlayer,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.Lorenzo]: createClient({
|
[EVMChain.Lorenzo]: createClient({
|
||||||
chain: lorenzo,
|
chain: lorenzo,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.Merlin]: createClient({
|
[EVMChain.Merlin]: createClient({
|
||||||
chain: merlin,
|
chain: merlin,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.AILayer]: createClient({
|
[EVMChain.AILayer]: createClient({
|
||||||
chain: ailayer,
|
chain: ailayer,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.Mode]: createClient({
|
[EVMChain.Mode]: createClient({
|
||||||
chain: mode,
|
chain: mode,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.XLayer]: createClient({
|
[EVMChain.XLayer]: createClient({
|
||||||
chain: xLayer,
|
chain: xLayer,
|
||||||
transport: fallback([http(), http("https://xlayerrpc.okx.com")]),
|
transport: fallback([http(), http("https://xlayerrpc.okx.com")]),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.Arbitrum]: createClient({
|
[EVMChain.Arbitrum]: createClient({
|
||||||
chain: arbitrum,
|
chain: arbitrum,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.Aurora]: createClient({
|
[EVMChain.Aurora]: createClient({
|
||||||
chain: aurora,
|
chain: aurora,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.Manta]: createClient({
|
[EVMChain.Manta]: createClient({
|
||||||
chain: manta,
|
chain: manta,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.Linea]: createClient({
|
[EVMChain.Linea]: createClient({
|
||||||
chain: linea,
|
chain: linea,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.Base]: createClient({
|
[EVMChain.Base]: createClient({
|
||||||
chain: base,
|
chain: base,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
batch: { multicall: true },
|
batch: { multicall: true },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.BlifeTestnet]: createClient({
|
[EVMChain.BlifeTestnet]: createClient({
|
||||||
chain: blifeTestnet,
|
chain: blifeTestnet,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[EVMChain.BeraTestnet]: createClient({
|
[EVMChain.BeraTestnet]: createClient({
|
||||||
chain: berachainTestnet,
|
chain: berachainTestnet,
|
||||||
transport: http(),
|
transport: http(),
|
||||||
}),
|
batch: { multicall: true },
|
||||||
|
}),
|
||||||
|
} satisfies Record<Exclude<EVMChain, typeof EVMChain.BitboyTestnet>, Client>
|
||||||
|
|
||||||
|
export const evmChainIdFromKnownChainId = (
|
||||||
|
chain: KnownChainId.EVMChain,
|
||||||
|
): undefined | bigint => {
|
||||||
|
const client = defaultEvmClients[chain]
|
||||||
|
const chainId = client?.chain?.id
|
||||||
|
if (chainId == null) return
|
||||||
|
return BigInt(chainId)
|
||||||
|
}
|
||||||
|
export const evmChainIdToKnownChainId = (
|
||||||
|
chainId: bigint,
|
||||||
|
): undefined | KnownChainId.EVMChain => {
|
||||||
|
return entries(defaultEvmClients).find(
|
||||||
|
([_, client]) => client?.chain?.id === Number(chainId),
|
||||||
|
)?.[0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { callReadOnlyFunction } from "@stacks/transactions"
|
import { unwrapResponse } from "clarity-codegen"
|
||||||
import { CallReadOnlyFunctionFn, unwrapResponse } from "clarity-codegen"
|
|
||||||
import { readContract } from "viem/actions"
|
import { readContract } from "viem/actions"
|
||||||
import {
|
import {
|
||||||
getBRC20SupportedRoutes,
|
getBRC20SupportedRoutes,
|
||||||
@@ -8,6 +7,7 @@ import {
|
|||||||
import { contractAssignedChainIdFromKnownChain } from "../stacksUtils/crossContractDataMapping"
|
import { contractAssignedChainIdFromKnownChain } from "../stacksUtils/crossContractDataMapping"
|
||||||
import {
|
import {
|
||||||
getTerminatingStacksTokenContractAddress,
|
getTerminatingStacksTokenContractAddress,
|
||||||
|
StacksContractName,
|
||||||
stxTokenContractAddresses,
|
stxTokenContractAddresses,
|
||||||
} from "../stacksUtils/stxContractAddresses"
|
} from "../stacksUtils/stxContractAddresses"
|
||||||
import {
|
import {
|
||||||
@@ -46,7 +46,7 @@ export const getEvm2StacksFeeInfo = async (
|
|||||||
): Promise<undefined | TransferProphet> => {
|
): Promise<undefined | TransferProphet> => {
|
||||||
const stacksContractCallInfo = getStacksContractCallInfo(
|
const stacksContractCallInfo = getStacksContractCallInfo(
|
||||||
route.toChain,
|
route.toChain,
|
||||||
"cross-peg-in-endpoint-v2-04",
|
StacksContractName.EVMPegInEndpoint,
|
||||||
)
|
)
|
||||||
const evmContractCallInfo = await getEVMContractCallInfo(ctx, route.fromChain)
|
const evmContractCallInfo = await getEVMContractCallInfo(ctx, route.fromChain)
|
||||||
const evmTokenContractCallInfo = await getEVMTokenContractInfo(
|
const evmTokenContractCallInfo = await getEVMTokenContractInfo(
|
||||||
@@ -66,15 +66,6 @@ export const getEvm2StacksFeeInfo = async (
|
|||||||
return getEvm2StacksNativeBridgeFeeInfo(ctx, route)
|
return getEvm2StacksNativeBridgeFeeInfo(ctx, route)
|
||||||
}
|
}
|
||||||
|
|
||||||
const executeOptions = {
|
|
||||||
deployerAddress: stacksContractCallInfo.deployerAddress,
|
|
||||||
callReadOnlyFunction: (callOptions =>
|
|
||||||
callReadOnlyFunction({
|
|
||||||
...callOptions,
|
|
||||||
network: stacksContractCallInfo.network,
|
|
||||||
})) satisfies CallReadOnlyFunctionFn,
|
|
||||||
}
|
|
||||||
|
|
||||||
const { client, tokenContractAddress } = evmTokenContractCallInfo
|
const { client, tokenContractAddress } = evmTokenContractCallInfo
|
||||||
|
|
||||||
const registryAddr =
|
const registryAddr =
|
||||||
@@ -126,7 +117,7 @@ export const getEvm2StacksFeeInfo = async (
|
|||||||
stacksContractCallInfo.contractName,
|
stacksContractCallInfo.contractName,
|
||||||
"get-paused",
|
"get-paused",
|
||||||
{},
|
{},
|
||||||
executeOptions,
|
stacksContractCallInfo.executeOptions,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -157,7 +148,7 @@ const getEvm2StacksNativeBridgeFeeInfo = async (
|
|||||||
): Promise<undefined | TransferProphet> => {
|
): Promise<undefined | TransferProphet> => {
|
||||||
const stacksContractCallInfo = getStacksContractCallInfo(
|
const stacksContractCallInfo = getStacksContractCallInfo(
|
||||||
route.toChain,
|
route.toChain,
|
||||||
"cross-peg-in-endpoint-v2-04",
|
StacksContractName.EVMPegInEndpoint,
|
||||||
)
|
)
|
||||||
const evmContractCallInfo = await getEVMContractCallInfo(ctx, route.fromChain)
|
const evmContractCallInfo = await getEVMContractCallInfo(ctx, route.fromChain)
|
||||||
if (
|
if (
|
||||||
@@ -167,21 +158,12 @@ const getEvm2StacksNativeBridgeFeeInfo = async (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const executeOptions = {
|
|
||||||
deployerAddress: stacksContractCallInfo.deployerAddress,
|
|
||||||
callReadOnlyFunction: (callOptions =>
|
|
||||||
callReadOnlyFunction({
|
|
||||||
...callOptions,
|
|
||||||
network: stacksContractCallInfo.network,
|
|
||||||
})) satisfies CallReadOnlyFunctionFn,
|
|
||||||
}
|
|
||||||
|
|
||||||
const resp = await props({
|
const resp = await props({
|
||||||
isPaused: executeReadonlyCallXLINK(
|
isPaused: executeReadonlyCallXLINK(
|
||||||
stacksContractCallInfo.contractName,
|
stacksContractCallInfo.contractName,
|
||||||
"get-paused",
|
"get-paused",
|
||||||
{},
|
{},
|
||||||
executeOptions,
|
stacksContractCallInfo.executeOptions,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -195,13 +177,15 @@ const getEvm2StacksNativeBridgeFeeInfo = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getStacks2EvmFeeInfo = async (
|
export const getStacks2EvmFeeInfo = async (
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
route: KnownRoute_FromStacks_ToEVM,
|
route: KnownRoute_FromStacks_ToEVM,
|
||||||
): Promise<undefined | TransferProphet> => {
|
): Promise<undefined | TransferProphet> => {
|
||||||
const stacksContractCallInfo = getStacksContractCallInfo(
|
const stacksContractCallInfo = getStacksContractCallInfo(
|
||||||
route.fromChain,
|
route.fromChain,
|
||||||
"cross-peg-out-endpoint-v2-01",
|
StacksContractName.EVMPegOutEndpoint,
|
||||||
)
|
)
|
||||||
const stacksTokenContractCallInfo = getStacksTokenContractInfo(
|
const stacksTokenContractCallInfo = await getStacksTokenContractInfo(
|
||||||
|
ctx,
|
||||||
route.fromChain,
|
route.fromChain,
|
||||||
route.fromToken,
|
route.fromToken,
|
||||||
)
|
)
|
||||||
@@ -214,15 +198,6 @@ export const getStacks2EvmFeeInfo = async (
|
|||||||
getTerminatingStacksTokenContractAddress(route) ??
|
getTerminatingStacksTokenContractAddress(route) ??
|
||||||
stacksTokenContractCallInfo
|
stacksTokenContractCallInfo
|
||||||
|
|
||||||
const executeOptions = {
|
|
||||||
deployerAddress: stacksContractCallInfo.deployerAddress,
|
|
||||||
callReadOnlyFunction: (callOptions =>
|
|
||||||
callReadOnlyFunction({
|
|
||||||
...callOptions,
|
|
||||||
network: stacksContractCallInfo.network,
|
|
||||||
})) satisfies CallReadOnlyFunctionFn,
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokenConf = await Promise.all([
|
const tokenConf = await Promise.all([
|
||||||
executeReadonlyCallXLINK(
|
executeReadonlyCallXLINK(
|
||||||
stacksContractCallInfo.contractName,
|
stacksContractCallInfo.contractName,
|
||||||
@@ -233,13 +208,13 @@ export const getStacks2EvmFeeInfo = async (
|
|||||||
"chain-id": toChainId,
|
"chain-id": toChainId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
executeOptions,
|
stacksContractCallInfo.executeOptions,
|
||||||
),
|
),
|
||||||
executeReadonlyCallXLINK(
|
executeReadonlyCallXLINK(
|
||||||
stacksContractCallInfo.contractName,
|
stacksContractCallInfo.contractName,
|
||||||
"get-paused",
|
"get-paused",
|
||||||
{},
|
{},
|
||||||
executeOptions,
|
stacksContractCallInfo.executeOptions,
|
||||||
),
|
),
|
||||||
]).then(([resp, isPaused]) => {
|
]).then(([resp, isPaused]) => {
|
||||||
if (resp.type !== "success") return undefined
|
if (resp.type !== "success") return undefined
|
||||||
@@ -281,7 +256,7 @@ export const getStacks2EvmFeeInfo = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fromCorrespondingStacksToken(
|
export async function evmTokenFromCorrespondingStacksToken(
|
||||||
toChain: KnownChainId.EVMChain,
|
toChain: KnownChainId.EVMChain,
|
||||||
stacksToken: KnownTokenId.StacksToken,
|
stacksToken: KnownTokenId.StacksToken,
|
||||||
): Promise<KnownTokenId.EVMToken[]> {
|
): Promise<KnownTokenId.EVMToken[]> {
|
||||||
@@ -348,7 +323,7 @@ export async function fromCorrespondingStacksToken(
|
|||||||
checkNever(restEVMTokenPossibilities)
|
checkNever(restEVMTokenPossibilities)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
export async function toCorrespondingStacksToken(
|
export async function evmTokenToCorrespondingStacksToken(
|
||||||
evmToken: KnownTokenId.EVMToken,
|
evmToken: KnownTokenId.EVMToken,
|
||||||
): Promise<undefined | KnownTokenId.StacksToken> {
|
): Promise<undefined | KnownTokenId.StacksToken> {
|
||||||
const EVMToken = KnownTokenId.EVM
|
const EVMToken = KnownTokenId.EVM
|
||||||
@@ -429,7 +404,7 @@ export const isSupportedEVMRoute: IsSupportedFn = async (ctx, route) => {
|
|||||||
if (KnownChainId.isStacksChain(toChain)) {
|
if (KnownChainId.isStacksChain(toChain)) {
|
||||||
if (!KnownTokenId.isStacksToken(toToken)) return false
|
if (!KnownTokenId.isStacksToken(toToken)) return false
|
||||||
|
|
||||||
const stacksToken = await toCorrespondingStacksToken(fromToken)
|
const stacksToken = await evmTokenToCorrespondingStacksToken(fromToken)
|
||||||
if (stacksToken == null) return false
|
if (stacksToken == null) return false
|
||||||
|
|
||||||
if (stxTokenContractAddresses[stacksToken]?.[toChain] == null) {
|
if (stxTokenContractAddresses[stacksToken]?.[toChain] == null) {
|
||||||
@@ -445,10 +420,11 @@ export const isSupportedEVMRoute: IsSupportedFn = async (ctx, route) => {
|
|||||||
const toTokenInfo = await getEVMTokenContractInfo(ctx, toChain, toToken)
|
const toTokenInfo = await getEVMTokenContractInfo(ctx, toChain, toToken)
|
||||||
if (toTokenInfo == null) return false
|
if (toTokenInfo == null) return false
|
||||||
|
|
||||||
const transitStacksToken = await toCorrespondingStacksToken(fromToken)
|
const transitStacksToken =
|
||||||
|
await evmTokenToCorrespondingStacksToken(fromToken)
|
||||||
if (transitStacksToken == null) return false
|
if (transitStacksToken == null) return false
|
||||||
|
|
||||||
const toEVMTokens = await fromCorrespondingStacksToken(
|
const toEVMTokens = await evmTokenFromCorrespondingStacksToken(
|
||||||
toChain,
|
toChain,
|
||||||
transitStacksToken,
|
transitStacksToken,
|
||||||
)
|
)
|
||||||
@@ -457,14 +433,15 @@ export const isSupportedEVMRoute: IsSupportedFn = async (ctx, route) => {
|
|||||||
|
|
||||||
if (KnownChainId.isBitcoinChain(toChain)) {
|
if (KnownChainId.isBitcoinChain(toChain)) {
|
||||||
if (!KnownTokenId.isBitcoinToken(toToken)) return false
|
if (!KnownTokenId.isBitcoinToken(toToken)) return false
|
||||||
const stacksToken = await toCorrespondingStacksToken(fromToken)
|
const stacksToken = await evmTokenToCorrespondingStacksToken(fromToken)
|
||||||
return stacksToken === KnownTokenId.Stacks.aBTC
|
return stacksToken === KnownTokenId.Stacks.aBTC
|
||||||
}
|
}
|
||||||
|
|
||||||
if (KnownChainId.isRunesChain(toChain)) {
|
if (KnownChainId.isRunesChain(toChain)) {
|
||||||
if (!KnownTokenId.isRunesToken(toToken)) return false
|
if (!KnownTokenId.isRunesToken(toToken)) return false
|
||||||
|
|
||||||
const transitStacksToken = await toCorrespondingStacksToken(fromToken)
|
const transitStacksToken =
|
||||||
|
await evmTokenToCorrespondingStacksToken(fromToken)
|
||||||
if (transitStacksToken == null) return false
|
if (transitStacksToken == null) return false
|
||||||
|
|
||||||
const runesRoutes = await getRunesSupportedRoutes(ctx, toChain)
|
const runesRoutes = await getRunesSupportedRoutes(ctx, toChain)
|
||||||
@@ -474,7 +451,8 @@ export const isSupportedEVMRoute: IsSupportedFn = async (ctx, route) => {
|
|||||||
if (KnownChainId.isBRC20Chain(toChain)) {
|
if (KnownChainId.isBRC20Chain(toChain)) {
|
||||||
if (!KnownTokenId.isBRC20Token(toToken)) return false
|
if (!KnownTokenId.isBRC20Token(toToken)) return false
|
||||||
|
|
||||||
const transitStacksToken = await toCorrespondingStacksToken(fromToken)
|
const transitStacksToken =
|
||||||
|
await evmTokenToCorrespondingStacksToken(fromToken)
|
||||||
if (transitStacksToken == null) return false
|
if (transitStacksToken == null) return false
|
||||||
|
|
||||||
const brc20Routes = await getBRC20SupportedRoutes(ctx, toChain)
|
const brc20Routes = await getBRC20SupportedRoutes(ctx, toChain)
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ const getOnChainConfigs = async (
|
|||||||
configContractAddress: Address,
|
configContractAddress: Address,
|
||||||
): Promise<undefined | EVMOnChainAddresses> => {
|
): Promise<undefined | EVMOnChainAddresses> => {
|
||||||
const cache = sdkContext.evm.onChainConfigCache
|
const cache = sdkContext.evm.onChainConfigCache
|
||||||
const cacheKey = `${chain}:${configContractAddress}`
|
const cacheKey = `${chain}:${configContractAddress}` as const
|
||||||
|
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
const cachedPromise = cache.get(cacheKey)
|
const cachedPromise = cache.get(cacheKey)
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ export {
|
|||||||
StacksContractAddress,
|
StacksContractAddress,
|
||||||
PublicEVMContractType as EVMContractType,
|
PublicEVMContractType as EVMContractType,
|
||||||
} from "./xlinkSdkUtils/types"
|
} from "./xlinkSdkUtils/types"
|
||||||
|
export {
|
||||||
|
SwapRoute,
|
||||||
|
SwapRoute_WithMinimumAmountsToReceive_Public as SwapRoute_WithMinimumAmountsOut,
|
||||||
|
SwapRoute_WithExchangeRate_Public as SwapRoute_WithExchangeRate,
|
||||||
|
} from "./utils/SwapRouteHelpers"
|
||||||
export { TimeLockedAsset } from "./xlinkSdkUtils/timelockFromEVM"
|
export { TimeLockedAsset } from "./xlinkSdkUtils/timelockFromEVM"
|
||||||
export {
|
export {
|
||||||
PublicTransferProphet as TransferProphet,
|
PublicTransferProphet as TransferProphet,
|
||||||
|
|||||||
@@ -4,9 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
fromCorrespondingStacksToken,
|
evmTokenFromCorrespondingStacksToken,
|
||||||
toCorrespondingStacksToken,
|
evmTokenToCorrespondingStacksToken,
|
||||||
} from "./evmUtils/peggingHelpers"
|
} from "./evmUtils/peggingHelpers"
|
||||||
|
import {
|
||||||
|
metaTokenFromCorrespondingStacksToken,
|
||||||
|
metaTokenToCorrespondingStacksToken,
|
||||||
|
} from "./metaUtils/peggingHelpers"
|
||||||
import { KnownChainId, KnownTokenId } from "./utils/types/knownIds"
|
import { KnownChainId, KnownTokenId } from "./utils/types/knownIds"
|
||||||
import { SDKGlobalContext } from "./xlinkSdkUtils/types.internal"
|
import { SDKGlobalContext } from "./xlinkSdkUtils/types.internal"
|
||||||
|
|
||||||
@@ -16,22 +20,31 @@ export {
|
|||||||
} from "./stacksUtils/crossContractDataMapping"
|
} from "./stacksUtils/crossContractDataMapping"
|
||||||
|
|
||||||
export {
|
export {
|
||||||
fromCorrespondingStacksToken as evmTokensFromCorrespondingStacksToken,
|
|
||||||
toCorrespondingStacksToken as evmTokenToCorrespondingStacksToken,
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
getTerminatingStacksTokenContractAddress,
|
|
||||||
getEVMTokenIdFromTerminatingStacksTokenContractAddress,
|
getEVMTokenIdFromTerminatingStacksTokenContractAddress,
|
||||||
|
getTerminatingStacksTokenContractAddress,
|
||||||
} from "./stacksUtils/stxContractAddresses"
|
} from "./stacksUtils/stxContractAddresses"
|
||||||
|
|
||||||
export {
|
|
||||||
addressFromBuffer,
|
|
||||||
addressToBuffer,
|
|
||||||
} from "./stacksUtils/xlinkContractHelpers"
|
|
||||||
|
|
||||||
export { isSupportedMetaRoute } from "./metaUtils/peggingHelpers"
|
export { isSupportedMetaRoute } from "./metaUtils/peggingHelpers"
|
||||||
|
export {
|
||||||
|
bridgeInfoFromMeta,
|
||||||
|
BridgeInfoFromMetaInput,
|
||||||
|
BridgeInfoFromMetaOutput,
|
||||||
|
} from "./xlinkSdkUtils/bridgeInfoFromMeta"
|
||||||
|
|
||||||
|
export {
|
||||||
|
brc20TokenFromTick,
|
||||||
|
brc20TokenToTick,
|
||||||
|
runesTokenFromId,
|
||||||
|
runesTokenToId,
|
||||||
|
} from "./metaUtils/tokenAddresses"
|
||||||
|
|
||||||
|
export { addressFromBuffer, addressToBuffer } from "./utils/addressHelpers"
|
||||||
|
|
||||||
|
export {
|
||||||
|
BridgeSwapRouteNode,
|
||||||
|
CreateBridgeOrderResult,
|
||||||
|
createBridgeOrderFromBitcoin,
|
||||||
|
} from "./stacksUtils/createBridgeOrderFromBitcoin"
|
||||||
export { bridgeFromEVM_toLaunchpad } from "./xlinkSdkUtils/bridgeFromEVM"
|
export { bridgeFromEVM_toLaunchpad } from "./xlinkSdkUtils/bridgeFromEVM"
|
||||||
|
|
||||||
export const getXLinkSDKContext = (
|
export const getXLinkSDKContext = (
|
||||||
@@ -46,7 +59,7 @@ export const evmTokensFromStacksToken = async (options: {
|
|||||||
}): Promise<{
|
}): Promise<{
|
||||||
evmTokens: KnownTokenId.EVMToken[]
|
evmTokens: KnownTokenId.EVMToken[]
|
||||||
}> => {
|
}> => {
|
||||||
const evmTokens = await fromCorrespondingStacksToken(
|
const evmTokens = await evmTokenFromCorrespondingStacksToken(
|
||||||
options.toEVMChain,
|
options.toEVMChain,
|
||||||
options.fromStacksToken,
|
options.fromStacksToken,
|
||||||
)
|
)
|
||||||
@@ -58,6 +71,44 @@ export const evmTokenToStacksToken = async (options: {
|
|||||||
}): Promise<{
|
}): Promise<{
|
||||||
stacksTokens: KnownTokenId.StacksToken[]
|
stacksTokens: KnownTokenId.StacksToken[]
|
||||||
}> => {
|
}> => {
|
||||||
const stacksTokens = await toCorrespondingStacksToken(options.fromEVMToken)
|
const stacksTokens = await evmTokenToCorrespondingStacksToken(
|
||||||
|
options.fromEVMToken,
|
||||||
|
)
|
||||||
|
return { stacksTokens: stacksTokens == null ? [] : [stacksTokens] }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const metaTokensFromStacksToken = async (
|
||||||
|
sdk: import("./XLinkSDK").XLinkSDK,
|
||||||
|
options: {
|
||||||
|
fromStacksToken: KnownTokenId.StacksToken
|
||||||
|
toChain: KnownChainId.BRC20Chain | KnownChainId.RunesChain
|
||||||
|
},
|
||||||
|
): Promise<{
|
||||||
|
tokens: (KnownTokenId.BRC20Token | KnownTokenId.RunesToken)[]
|
||||||
|
}> => {
|
||||||
|
const metaTokens = await metaTokenFromCorrespondingStacksToken(
|
||||||
|
getXLinkSDKContext(sdk),
|
||||||
|
options.toChain,
|
||||||
|
options.fromStacksToken,
|
||||||
|
)
|
||||||
|
return { tokens: metaTokens == null ? [] : [metaTokens as any] }
|
||||||
|
}
|
||||||
|
export const metaTokenToStacksToken = async (
|
||||||
|
sdk: import("./XLinkSDK").XLinkSDK,
|
||||||
|
options: {
|
||||||
|
fromChain: KnownChainId.BRC20Chain | KnownChainId.RunesChain
|
||||||
|
fromToken: KnownTokenId.BRC20Token | KnownTokenId.RunesToken
|
||||||
|
toStacksChain: KnownChainId.StacksChain
|
||||||
|
},
|
||||||
|
): Promise<{
|
||||||
|
stacksTokens: KnownTokenId.StacksToken[]
|
||||||
|
}> => {
|
||||||
|
const stacksTokens = await metaTokenToCorrespondingStacksToken(
|
||||||
|
getXLinkSDKContext(sdk),
|
||||||
|
{
|
||||||
|
chain: options.fromChain as any,
|
||||||
|
token: options.fromToken as any,
|
||||||
|
},
|
||||||
|
)
|
||||||
return { stacksTokens: stacksTokens == null ? [] : [stacksTokens] }
|
return { stacksTokens: stacksTokens == null ? [] : [stacksTokens] }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,25 @@
|
|||||||
import { toCorrespondingStacksToken } from "../evmUtils/peggingHelpers"
|
import { evmTokenToCorrespondingStacksToken } from "../evmUtils/peggingHelpers"
|
||||||
import { getEVMTokenContractInfo } from "../evmUtils/xlinkContractHelpers"
|
import { getEVMTokenContractInfo } from "../evmUtils/xlinkContractHelpers"
|
||||||
|
import { StacksContractName } from "../stacksUtils/stxContractAddresses"
|
||||||
|
import {
|
||||||
|
executeReadonlyCallXLINK,
|
||||||
|
getStacksContractCallInfo,
|
||||||
|
numberFromStacksContractNumber,
|
||||||
|
} from "../stacksUtils/xlinkContractHelpers"
|
||||||
import { BigNumber } from "../utils/BigNumber"
|
import { BigNumber } from "../utils/BigNumber"
|
||||||
import {
|
import {
|
||||||
_KnownRoute_FromBRC20_ToStacks,
|
getFinalStepStacksTokenAddress,
|
||||||
_KnownRoute_FromRunes_ToStacks,
|
getFirstStepStacksTokenAddress,
|
||||||
|
SwapRoute,
|
||||||
|
} from "../utils/SwapRouteHelpers"
|
||||||
|
import {
|
||||||
IsSupportedFn,
|
IsSupportedFn,
|
||||||
|
KnownRoute_FromBRC20_ToStacks,
|
||||||
|
KnownRoute_FromRunes_ToStacks,
|
||||||
KnownRoute_FromStacks_ToBRC20,
|
KnownRoute_FromStacks_ToBRC20,
|
||||||
KnownRoute_FromStacks_ToRunes,
|
KnownRoute_FromStacks_ToRunes,
|
||||||
} from "../utils/buildSupportedRoutes"
|
} from "../utils/buildSupportedRoutes"
|
||||||
|
import { props } from "../utils/promiseHelpers"
|
||||||
import { checkNever, isNotNull } from "../utils/typeHelpers"
|
import { checkNever, isNotNull } from "../utils/typeHelpers"
|
||||||
import { TransferProphet } from "../utils/types/TransferProphet"
|
import { TransferProphet } from "../utils/types/TransferProphet"
|
||||||
import {
|
import {
|
||||||
@@ -18,15 +30,62 @@ import {
|
|||||||
import { SDKGlobalContext } from "../xlinkSdkUtils/types.internal"
|
import { SDKGlobalContext } from "../xlinkSdkUtils/types.internal"
|
||||||
import { getMetaPegInAddress } from "./btcAddresses"
|
import { getMetaPegInAddress } from "./btcAddresses"
|
||||||
import {
|
import {
|
||||||
BRC20SupportedRoute,
|
|
||||||
getBRC20SupportedRoutes,
|
getBRC20SupportedRoutes,
|
||||||
getRunesSupportedRoutes,
|
getRunesSupportedRoutes,
|
||||||
RunesSupportedRoute,
|
|
||||||
} from "./xlinkContractHelpers"
|
} from "./xlinkContractHelpers"
|
||||||
|
|
||||||
|
export async function metaTokenFromCorrespondingStacksToken(
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
|
chain: KnownChainId.BRC20Chain | KnownChainId.RunesChain,
|
||||||
|
stacksToken: KnownTokenId.StacksToken,
|
||||||
|
): Promise<undefined | KnownTokenId.BRC20Token | KnownTokenId.RunesToken> {
|
||||||
|
if (KnownChainId.isBRC20Chain(chain)) {
|
||||||
|
const routes = await getBRC20SupportedRoutes(ctx, chain)
|
||||||
|
return routes.find(r => r.stacksToken === stacksToken)?.brc20Token
|
||||||
|
} else if (KnownChainId.isRunesChain(chain)) {
|
||||||
|
const routes = await getRunesSupportedRoutes(ctx, chain)
|
||||||
|
return routes.find(r => r.stacksToken === stacksToken)?.runesToken
|
||||||
|
} else {
|
||||||
|
checkNever(chain)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function metaTokenToCorrespondingStacksToken(
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
|
route:
|
||||||
|
| { chain: KnownChainId.BRC20Chain; token: KnownTokenId.BRC20Token }
|
||||||
|
| { chain: KnownChainId.RunesChain; token: KnownTokenId.RunesToken },
|
||||||
|
): Promise<undefined | KnownTokenId.StacksToken> {
|
||||||
|
if (KnownChainId.isBRC20Chain(route.chain)) {
|
||||||
|
const routes = await getBRC20SupportedRoutes(ctx, route.chain)
|
||||||
|
return routes.find(r => r.brc20Token === route.token)?.stacksToken
|
||||||
|
} else if (KnownChainId.isRunesChain(route.chain)) {
|
||||||
|
const routes = await getRunesSupportedRoutes(ctx, route.chain)
|
||||||
|
return routes.find(r => r.runesToken === route.token)?.stacksToken
|
||||||
|
} else {
|
||||||
|
checkNever(route.chain)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const getMeta2StacksFeeInfo = async (
|
export const getMeta2StacksFeeInfo = async (
|
||||||
ctx: SDKGlobalContext,
|
ctx: SDKGlobalContext,
|
||||||
route: _KnownRoute_FromBRC20_ToStacks | _KnownRoute_FromRunes_ToStacks,
|
route: KnownRoute_FromBRC20_ToStacks | KnownRoute_FromRunes_ToStacks,
|
||||||
|
options: {
|
||||||
|
swapRoute: null | SwapRoute
|
||||||
|
},
|
||||||
|
): Promise<undefined | TransferProphet> => {
|
||||||
|
if (options.swapRoute != null) {
|
||||||
|
return getMeta2StacksSwapFeeInfo(route)
|
||||||
|
} else {
|
||||||
|
return getMeta2StacksBaseFeeInfo(ctx, route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMeta2StacksBaseFeeInfo = async (
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
|
route: KnownRoute_FromBRC20_ToStacks | KnownRoute_FromRunes_ToStacks,
|
||||||
): Promise<undefined | TransferProphet> => {
|
): Promise<undefined | TransferProphet> => {
|
||||||
const filteredRoutes = KnownChainId.isBRC20Chain(route.fromChain)
|
const filteredRoutes = KnownChainId.isBRC20Chain(route.fromChain)
|
||||||
? await getBRC20SupportedRoutes(ctx, route.fromChain).then(routes =>
|
? await getBRC20SupportedRoutes(ctx, route.fromChain).then(routes =>
|
||||||
@@ -60,7 +119,7 @@ export const getMeta2StacksFeeInfo = async (
|
|||||||
? null
|
? null
|
||||||
: {
|
: {
|
||||||
type: "fixed" as const,
|
type: "fixed" as const,
|
||||||
token: KnownTokenId.Stacks.aBTC,
|
token: KnownTokenId.Bitcoin.BTC,
|
||||||
amount: filteredRoute.pegInFeeBitcoinAmount,
|
amount: filteredRoute.pegInFeeBitcoinAmount,
|
||||||
},
|
},
|
||||||
].filter(isNotNull),
|
].filter(isNotNull),
|
||||||
@@ -69,6 +128,47 @@ export const getMeta2StacksFeeInfo = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getMeta2StacksSwapFeeInfo = async (
|
||||||
|
route1: KnownRoute_FromBRC20_ToStacks | KnownRoute_FromRunes_ToStacks,
|
||||||
|
): Promise<undefined | TransferProphet> => {
|
||||||
|
const stacksSwapContractCallInfo = getStacksContractCallInfo(
|
||||||
|
route1.toChain,
|
||||||
|
StacksContractName.MetaPegInEndpointSwap,
|
||||||
|
)
|
||||||
|
if (stacksSwapContractCallInfo == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const resp = await props({
|
||||||
|
isPaused: executeReadonlyCallXLINK(
|
||||||
|
stacksSwapContractCallInfo.contractName,
|
||||||
|
"is-paused",
|
||||||
|
{},
|
||||||
|
stacksSwapContractCallInfo.executeOptions,
|
||||||
|
),
|
||||||
|
fixedBtcFee: executeReadonlyCallXLINK(
|
||||||
|
stacksSwapContractCallInfo.contractName,
|
||||||
|
"get-peg-in-fee",
|
||||||
|
{},
|
||||||
|
stacksSwapContractCallInfo.executeOptions,
|
||||||
|
).then(numberFromStacksContractNumber),
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
isPaused: resp.isPaused,
|
||||||
|
bridgeToken: route1.fromToken,
|
||||||
|
fees: [
|
||||||
|
{
|
||||||
|
type: "fixed" as const,
|
||||||
|
token: KnownTokenId.Bitcoin.BTC,
|
||||||
|
amount: resp.fixedBtcFee,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
minBridgeAmount: BigNumber.ZERO,
|
||||||
|
maxBridgeAmount: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const getStacks2MetaFeeInfo = async (
|
export const getStacks2MetaFeeInfo = async (
|
||||||
ctx: SDKGlobalContext,
|
ctx: SDKGlobalContext,
|
||||||
route: KnownRoute_FromStacks_ToBRC20 | KnownRoute_FromStacks_ToRunes,
|
route: KnownRoute_FromStacks_ToBRC20 | KnownRoute_FromStacks_ToRunes,
|
||||||
@@ -105,7 +205,7 @@ export const getStacks2MetaFeeInfo = async (
|
|||||||
? null
|
? null
|
||||||
: {
|
: {
|
||||||
type: "fixed" as const,
|
type: "fixed" as const,
|
||||||
token: KnownTokenId.Stacks.aBTC,
|
token: KnownTokenId.Bitcoin.BTC,
|
||||||
amount: filteredRoute.pegOutFeeBitcoinAmount,
|
amount: filteredRoute.pegOutFeeBitcoinAmount,
|
||||||
},
|
},
|
||||||
].filter(isNotNull),
|
].filter(isNotNull),
|
||||||
@@ -158,13 +258,23 @@ export const isSupportedMetaRoute: IsSupportedFn = async (ctx, route) => {
|
|||||||
if (!KnownTokenId.isStacksToken(toToken)) return false
|
if (!KnownTokenId.isStacksToken(toToken)) return false
|
||||||
|
|
||||||
if (KnownChainId.isRunesChain(fromChain)) {
|
if (KnownChainId.isRunesChain(fromChain)) {
|
||||||
|
if (!KnownTokenId.isRunesToken(fromToken)) return false
|
||||||
|
|
||||||
const runesRoutes = await getRunesSupportedRoutes(ctx, fromChain)
|
const runesRoutes = await getRunesSupportedRoutes(ctx, fromChain)
|
||||||
return runesRoutes.some(route => route.stacksToken === toToken)
|
return runesRoutes.some(
|
||||||
|
route =>
|
||||||
|
route.runesToken === fromToken && route.stacksToken === toToken,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (KnownChainId.isBRC20Chain(fromChain)) {
|
if (KnownChainId.isBRC20Chain(fromChain)) {
|
||||||
|
if (!KnownTokenId.isBRC20Token(fromToken)) return false
|
||||||
|
|
||||||
const brc20Routes = await getBRC20SupportedRoutes(ctx, fromChain)
|
const brc20Routes = await getBRC20SupportedRoutes(ctx, fromChain)
|
||||||
return brc20Routes.some(route => route.stacksToken === toToken)
|
return brc20Routes.some(
|
||||||
|
route =>
|
||||||
|
route.brc20Token === fromToken && route.stacksToken === toToken,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkNever(fromChain)
|
checkNever(fromChain)
|
||||||
@@ -177,17 +287,29 @@ export const isSupportedMetaRoute: IsSupportedFn = async (ctx, route) => {
|
|||||||
const info = await getEVMTokenContractInfo(ctx, toChain, toToken)
|
const info = await getEVMTokenContractInfo(ctx, toChain, toToken)
|
||||||
if (info == null) return false
|
if (info == null) return false
|
||||||
|
|
||||||
const transitStacksToken = await toCorrespondingStacksToken(toToken)
|
const transitStacksToken = await evmTokenToCorrespondingStacksToken(toToken)
|
||||||
if (transitStacksToken == null) return false
|
if (transitStacksToken == null) return false
|
||||||
|
|
||||||
if (KnownChainId.isRunesChain(fromChain)) {
|
if (KnownChainId.isRunesChain(fromChain)) {
|
||||||
|
if (!KnownTokenId.isRunesToken(fromToken)) return false
|
||||||
|
|
||||||
const runesRoutes = await getRunesSupportedRoutes(ctx, fromChain)
|
const runesRoutes = await getRunesSupportedRoutes(ctx, fromChain)
|
||||||
return runesRoutes.some(route => route.stacksToken === transitStacksToken)
|
return runesRoutes.some(
|
||||||
|
route =>
|
||||||
|
route.runesToken === fromToken &&
|
||||||
|
route.stacksToken === transitStacksToken,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (KnownChainId.isBRC20Chain(fromChain)) {
|
if (KnownChainId.isBRC20Chain(fromChain)) {
|
||||||
|
if (!KnownTokenId.isBRC20Token(fromToken)) return false
|
||||||
|
|
||||||
const brc20Routes = await getBRC20SupportedRoutes(ctx, fromChain)
|
const brc20Routes = await getBRC20SupportedRoutes(ctx, fromChain)
|
||||||
return brc20Routes.some(route => route.stacksToken === transitStacksToken)
|
return brc20Routes.some(
|
||||||
|
route =>
|
||||||
|
route.brc20Token === fromToken &&
|
||||||
|
route.stacksToken === transitStacksToken,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkNever(fromChain)
|
checkNever(fromChain)
|
||||||
@@ -197,39 +319,205 @@ export const isSupportedMetaRoute: IsSupportedFn = async (ctx, route) => {
|
|||||||
if (KnownChainId.isRunesChain(toChain)) {
|
if (KnownChainId.isRunesChain(toChain)) {
|
||||||
if (!KnownTokenId.isRunesToken(toToken)) return false
|
if (!KnownTokenId.isRunesToken(toToken)) return false
|
||||||
|
|
||||||
// TODO: runes -> runes (swap) is not supported yet
|
const finalStepStacksToken =
|
||||||
if (KnownChainId.isRunesChain(fromChain)) return false
|
route.swapRoute == null
|
||||||
|
? await metaTokenToCorrespondingStacksToken(ctx, {
|
||||||
|
chain: toChain,
|
||||||
|
token: toToken,
|
||||||
|
})
|
||||||
|
: await getFinalStepStacksTokenAddress(ctx, {
|
||||||
|
swap: route.swapRoute,
|
||||||
|
stacksChain:
|
||||||
|
toChain === KnownChainId.Runes.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
})
|
||||||
|
if (finalStepStacksToken == null) return false
|
||||||
|
|
||||||
const brc20Routes = await getBRC20SupportedRoutes(ctx, fromChain)
|
// runes -> runes
|
||||||
const runesRoutes = await getRunesSupportedRoutes(ctx, toChain)
|
if (KnownChainId.isRunesChain(fromChain)) {
|
||||||
return getRoutesOverlapping(brc20Routes, runesRoutes) != null
|
if (!KnownTokenId.isRunesToken(fromToken)) return false
|
||||||
|
|
||||||
|
const firstStepStacksToken =
|
||||||
|
route.swapRoute == null
|
||||||
|
? await metaTokenToCorrespondingStacksToken(ctx, {
|
||||||
|
chain: fromChain,
|
||||||
|
token: fromToken,
|
||||||
|
})
|
||||||
|
: await getFirstStepStacksTokenAddress(ctx, {
|
||||||
|
swap: route.swapRoute,
|
||||||
|
stacksChain:
|
||||||
|
fromChain === KnownChainId.Runes.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
})
|
||||||
|
if (firstStepStacksToken == null) return false
|
||||||
|
|
||||||
|
if (route.swapRoute == null) {
|
||||||
|
return firstStepStacksToken === finalStepStacksToken
|
||||||
|
}
|
||||||
|
|
||||||
|
const runesRoutes = await getRunesSupportedRoutes(ctx, fromChain)
|
||||||
|
|
||||||
|
return (
|
||||||
|
runesRoutes.find(
|
||||||
|
route =>
|
||||||
|
route.runesToken === fromToken &&
|
||||||
|
route.stacksToken === firstStepStacksToken,
|
||||||
|
) != null &&
|
||||||
|
runesRoutes.find(
|
||||||
|
route =>
|
||||||
|
route.runesToken === toToken &&
|
||||||
|
route.stacksToken === finalStepStacksToken,
|
||||||
|
) != null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// brc20 -> runes
|
||||||
|
if (KnownChainId.isBRC20Chain(fromChain)) {
|
||||||
|
if (!KnownTokenId.isBRC20Token(fromToken)) return false
|
||||||
|
|
||||||
|
const firstStepStacksToken =
|
||||||
|
route.swapRoute == null
|
||||||
|
? await metaTokenToCorrespondingStacksToken(ctx, {
|
||||||
|
chain: fromChain,
|
||||||
|
token: fromToken,
|
||||||
|
})
|
||||||
|
: await getFirstStepStacksTokenAddress(ctx, {
|
||||||
|
swap: route.swapRoute,
|
||||||
|
stacksChain:
|
||||||
|
fromChain === KnownChainId.BRC20.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
})
|
||||||
|
if (firstStepStacksToken == null) return false
|
||||||
|
|
||||||
|
if (route.swapRoute == null) {
|
||||||
|
return firstStepStacksToken === finalStepStacksToken
|
||||||
|
}
|
||||||
|
|
||||||
|
const fromRoutes = await getBRC20SupportedRoutes(ctx, fromChain)
|
||||||
|
const toRoutes = await getRunesSupportedRoutes(ctx, toChain)
|
||||||
|
|
||||||
|
return (
|
||||||
|
fromRoutes.find(
|
||||||
|
route =>
|
||||||
|
route.brc20Token === fromToken &&
|
||||||
|
route.stacksToken === firstStepStacksToken,
|
||||||
|
) != null &&
|
||||||
|
toRoutes.find(
|
||||||
|
route =>
|
||||||
|
route.runesToken === toToken &&
|
||||||
|
route.stacksToken === finalStepStacksToken,
|
||||||
|
) != null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNever(fromChain)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (KnownChainId.isBRC20Chain(toChain)) {
|
if (KnownChainId.isBRC20Chain(toChain)) {
|
||||||
if (!KnownTokenId.isBRC20Token(toToken)) return false
|
if (!KnownTokenId.isBRC20Token(toToken)) return false
|
||||||
|
|
||||||
// TODO: brc20 -> brc20 (swap) is not supported yet
|
const finalStepStacksToken =
|
||||||
if (KnownChainId.isBRC20Chain(fromChain)) return false
|
route.swapRoute == null
|
||||||
|
? await metaTokenToCorrespondingStacksToken(ctx, {
|
||||||
|
chain: toChain,
|
||||||
|
token: toToken,
|
||||||
|
})
|
||||||
|
: await getFinalStepStacksTokenAddress(ctx, {
|
||||||
|
swap: route.swapRoute,
|
||||||
|
stacksChain:
|
||||||
|
toChain === KnownChainId.BRC20.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
})
|
||||||
|
if (finalStepStacksToken == null) return false
|
||||||
|
|
||||||
const brc20Routes = await getBRC20SupportedRoutes(ctx, toChain)
|
// brc20 -> brc20
|
||||||
const runesRoutes = await getRunesSupportedRoutes(ctx, fromChain)
|
if (KnownChainId.isBRC20Chain(fromChain)) {
|
||||||
return getRoutesOverlapping(brc20Routes, runesRoutes) != null
|
if (!KnownTokenId.isBRC20Token(fromToken)) return false
|
||||||
|
|
||||||
|
const firstStepStacksToken =
|
||||||
|
route.swapRoute == null
|
||||||
|
? await metaTokenToCorrespondingStacksToken(ctx, {
|
||||||
|
chain: fromChain,
|
||||||
|
token: fromToken,
|
||||||
|
})
|
||||||
|
: await getFirstStepStacksTokenAddress(ctx, {
|
||||||
|
swap: route.swapRoute,
|
||||||
|
stacksChain:
|
||||||
|
fromChain === KnownChainId.BRC20.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
})
|
||||||
|
if (firstStepStacksToken == null) return false
|
||||||
|
|
||||||
|
if (route.swapRoute == null) {
|
||||||
|
return firstStepStacksToken === finalStepStacksToken
|
||||||
|
}
|
||||||
|
|
||||||
|
const brc20Routes = await getBRC20SupportedRoutes(ctx, toChain)
|
||||||
|
|
||||||
|
return (
|
||||||
|
brc20Routes.find(
|
||||||
|
route =>
|
||||||
|
route.brc20Token === fromToken &&
|
||||||
|
route.stacksToken === firstStepStacksToken,
|
||||||
|
) != null &&
|
||||||
|
brc20Routes.find(
|
||||||
|
route =>
|
||||||
|
route.brc20Token === toToken &&
|
||||||
|
route.stacksToken === finalStepStacksToken,
|
||||||
|
) != null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runes -> brc20
|
||||||
|
if (KnownChainId.isRunesChain(fromChain)) {
|
||||||
|
if (!KnownTokenId.isRunesToken(fromToken)) return false
|
||||||
|
|
||||||
|
const firstStepStacksToken =
|
||||||
|
route.swapRoute == null
|
||||||
|
? await metaTokenToCorrespondingStacksToken(ctx, {
|
||||||
|
chain: fromChain,
|
||||||
|
token: fromToken,
|
||||||
|
})
|
||||||
|
: await getFirstStepStacksTokenAddress(ctx, {
|
||||||
|
swap: route.swapRoute,
|
||||||
|
stacksChain:
|
||||||
|
fromChain === KnownChainId.Runes.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
})
|
||||||
|
if (firstStepStacksToken == null) return false
|
||||||
|
|
||||||
|
if (route.swapRoute == null) {
|
||||||
|
return firstStepStacksToken === finalStepStacksToken
|
||||||
|
}
|
||||||
|
|
||||||
|
const fromRoutes = await getRunesSupportedRoutes(ctx, fromChain)
|
||||||
|
const toRoutes = await getBRC20SupportedRoutes(ctx, toChain)
|
||||||
|
|
||||||
|
return (
|
||||||
|
fromRoutes.find(
|
||||||
|
route =>
|
||||||
|
route.runesToken === fromToken &&
|
||||||
|
route.stacksToken === firstStepStacksToken,
|
||||||
|
) != null &&
|
||||||
|
toRoutes.find(
|
||||||
|
route =>
|
||||||
|
route.brc20Token === toToken &&
|
||||||
|
route.stacksToken === finalStepStacksToken,
|
||||||
|
) != null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNever(fromChain)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
checkNever(toChain)
|
checkNever(toChain)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const getRoutesOverlapping = (
|
|
||||||
brc20Routes: BRC20SupportedRoute[],
|
|
||||||
runesRoutes: RunesSupportedRoute[],
|
|
||||||
): null | [BRC20SupportedRoute, RunesSupportedRoute] => {
|
|
||||||
for (const brc20Route of brc20Routes) {
|
|
||||||
for (const runesRoute of runesRoutes) {
|
|
||||||
if (brc20Route.stacksToken === runesRoute.stacksToken) {
|
|
||||||
return [brc20Route, runesRoute]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|||||||
59
src/metaUtils/tokenAddresses.ts
Normal file
59
src/metaUtils/tokenAddresses.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { KnownChainId, KnownTokenId } from "../utils/types/knownIds"
|
||||||
|
import { SDKGlobalContext } from "../xlinkSdkUtils/types.internal"
|
||||||
|
import {
|
||||||
|
getBRC20SupportedRoutes,
|
||||||
|
getRunesSupportedRoutes,
|
||||||
|
} from "./xlinkContractHelpers"
|
||||||
|
|
||||||
|
export const brc20TokenFromTick = async (
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
|
chain: KnownChainId.BRC20Chain,
|
||||||
|
tick: string,
|
||||||
|
): Promise<undefined | { token: KnownTokenId.BRC20Token }> => {
|
||||||
|
const routes = await getBRC20SupportedRoutes(sdkContext, chain)
|
||||||
|
|
||||||
|
const token = routes.find(
|
||||||
|
route => route.brc20Tick.toLowerCase() === tick.toLowerCase(),
|
||||||
|
)?.brc20Token
|
||||||
|
|
||||||
|
if (token == null) return undefined
|
||||||
|
return { token }
|
||||||
|
}
|
||||||
|
export const brc20TokenToTick = async (
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
|
chain: KnownChainId.BRC20Chain,
|
||||||
|
token: KnownTokenId.BRC20Token,
|
||||||
|
): Promise<undefined | { tick: string }> => {
|
||||||
|
const routes = await getBRC20SupportedRoutes(sdkContext, chain)
|
||||||
|
const route = routes.find(route => route.brc20Token === token)
|
||||||
|
if (route == null) return undefined
|
||||||
|
return { tick: route.brc20Tick }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const runesTokenFromId = async (
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
|
chain: KnownChainId.RunesChain,
|
||||||
|
id: { blockHeight: bigint; txIndex: bigint },
|
||||||
|
): Promise<undefined | { token: KnownTokenId.RunesToken }> => {
|
||||||
|
const routes = await getRunesSupportedRoutes(sdkContext, chain)
|
||||||
|
|
||||||
|
const idString = `${id.blockHeight}:${id.txIndex}`
|
||||||
|
const token = routes.find(route => route.runesId === idString)?.runesToken
|
||||||
|
|
||||||
|
if (token == null) return undefined
|
||||||
|
return { token }
|
||||||
|
}
|
||||||
|
export const runesTokenToId = async (
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
|
chain: KnownChainId.RunesChain,
|
||||||
|
token: KnownTokenId.RunesToken,
|
||||||
|
): Promise<undefined | { id: { blockHeight: bigint; txIndex: bigint } }> => {
|
||||||
|
const routes = await getRunesSupportedRoutes(sdkContext, chain)
|
||||||
|
const route = routes.find(route => route.runesToken === token)
|
||||||
|
if (route == null) return undefined
|
||||||
|
|
||||||
|
const id = route.runesId.split(":") as [`${number}`, `${number}`]
|
||||||
|
return {
|
||||||
|
id: { blockHeight: BigInt(id[0]), txIndex: BigInt(id[1]) },
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,7 +43,7 @@ export async function getBRC20SupportedRoutes(
|
|||||||
return promise
|
return promise
|
||||||
}
|
}
|
||||||
async function _getBRC20SupportedRoutes(
|
async function _getBRC20SupportedRoutes(
|
||||||
sdkContext: Pick<SDKGlobalContext, "backendAPI">,
|
sdkContext: SDKGlobalContext,
|
||||||
chainId: KnownChainId.BRC20Chain,
|
chainId: KnownChainId.BRC20Chain,
|
||||||
): Promise<BRC20SupportedRoute[]> {
|
): Promise<BRC20SupportedRoute[]> {
|
||||||
const stacksChainId =
|
const stacksChainId =
|
||||||
@@ -65,6 +65,7 @@ async function _getBRC20SupportedRoutes(
|
|||||||
const routes = await Promise.all(
|
const routes = await Promise.all(
|
||||||
resp.routes.map(async (route): Promise<null | BRC20SupportedRoute> => {
|
resp.routes.map(async (route): Promise<null | BRC20SupportedRoute> => {
|
||||||
const stacksToken = await getStacksToken(
|
const stacksToken = await getStacksToken(
|
||||||
|
sdkContext,
|
||||||
stacksChainId,
|
stacksChainId,
|
||||||
route.stacksTokenContractAddress,
|
route.stacksTokenContractAddress,
|
||||||
)
|
)
|
||||||
@@ -118,7 +119,6 @@ export interface RunesSupportedRoute {
|
|||||||
pegOutFeeRate: BigNumber
|
pegOutFeeRate: BigNumber
|
||||||
pegOutFeeBitcoinAmount: null | BigNumber
|
pegOutFeeBitcoinAmount: null | BigNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRunesSupportedRoutes(
|
export async function getRunesSupportedRoutes(
|
||||||
sdkContext: SDKGlobalContext,
|
sdkContext: SDKGlobalContext,
|
||||||
chainId: KnownChainId.RunesChain,
|
chainId: KnownChainId.RunesChain,
|
||||||
@@ -141,7 +141,7 @@ export async function getRunesSupportedRoutes(
|
|||||||
return promise
|
return promise
|
||||||
}
|
}
|
||||||
async function _getRunesSupportedRoutes(
|
async function _getRunesSupportedRoutes(
|
||||||
sdkContext: Pick<SDKGlobalContext, "backendAPI">,
|
sdkContext: SDKGlobalContext,
|
||||||
chainId: KnownChainId.RunesChain,
|
chainId: KnownChainId.RunesChain,
|
||||||
): Promise<RunesSupportedRoute[]> {
|
): Promise<RunesSupportedRoute[]> {
|
||||||
const stacksChainId =
|
const stacksChainId =
|
||||||
@@ -163,6 +163,7 @@ async function _getRunesSupportedRoutes(
|
|||||||
const routes = await Promise.all(
|
const routes = await Promise.all(
|
||||||
resp.routes.map(async (route): Promise<null | RunesSupportedRoute> => {
|
resp.routes.map(async (route): Promise<null | RunesSupportedRoute> => {
|
||||||
const stacksToken = await getStacksToken(
|
const stacksToken = await getStacksToken(
|
||||||
|
sdkContext,
|
||||||
stacksChainId,
|
stacksChainId,
|
||||||
route.stacksTokenContractAddress,
|
route.stacksTokenContractAddress,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,186 +0,0 @@
|
|||||||
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!,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
406
src/stacksUtils/createBridgeOrderFromBitcoin.ts
Normal file
406
src/stacksUtils/createBridgeOrderFromBitcoin.ts
Normal file
@@ -0,0 +1,406 @@
|
|||||||
|
import { unwrapResponse } from "clarity-codegen"
|
||||||
|
import { evmTokenToCorrespondingStacksToken } from "../evmUtils/peggingHelpers"
|
||||||
|
import {
|
||||||
|
getBRC20SupportedRoutes,
|
||||||
|
getRunesSupportedRoutes,
|
||||||
|
} from "../metaUtils/xlinkContractHelpers"
|
||||||
|
import {
|
||||||
|
KnownRoute_FromBitcoin_ToBRC20,
|
||||||
|
KnownRoute_FromBitcoin_ToRunes,
|
||||||
|
} from "../utils/buildSupportedRoutes"
|
||||||
|
import { UnsupportedBridgeRouteError } from "../utils/errors"
|
||||||
|
import { decodeHex } from "../utils/hexHelpers"
|
||||||
|
import { SwapRoute_WithMinimumAmountsToReceive } from "../utils/SwapRouteHelpers"
|
||||||
|
import { assertExclude, checkNever } from "../utils/typeHelpers"
|
||||||
|
import { KnownChainId, KnownTokenId } from "../utils/types/knownIds"
|
||||||
|
import { StacksContractAddress } from "../xlinkSdkUtils/types"
|
||||||
|
import { SDKGlobalContext } from "../xlinkSdkUtils/types.internal"
|
||||||
|
import { contractAssignedChainIdFromKnownChain } from "./crossContractDataMapping"
|
||||||
|
import {
|
||||||
|
getTerminatingStacksTokenContractAddress,
|
||||||
|
StacksContractName,
|
||||||
|
} from "./stxContractAddresses"
|
||||||
|
import {
|
||||||
|
executeReadonlyCallXLINK,
|
||||||
|
getStacksContractCallInfo,
|
||||||
|
getStacksTokenContractInfo,
|
||||||
|
numberToStacksContractNumber,
|
||||||
|
} from "./xlinkContractHelpers"
|
||||||
|
import { addressToBuffer } from "../utils/addressHelpers"
|
||||||
|
|
||||||
|
export interface BridgeSwapRouteNode {
|
||||||
|
poolId: bigint
|
||||||
|
tokenAddress: StacksContractAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateBridgeOrderResult {
|
||||||
|
terminatingStacksToken: StacksContractAddress
|
||||||
|
data: Uint8Array
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createBridgeOrderFromBitcoin(
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
|
info: {
|
||||||
|
fromChain: KnownChainId.BitcoinChain
|
||||||
|
fromBitcoinScriptPubKey: Uint8Array
|
||||||
|
toChain:
|
||||||
|
| KnownChainId.StacksChain
|
||||||
|
| KnownChainId.EVMChain
|
||||||
|
| KnownChainId.BRC20Chain
|
||||||
|
| KnownChainId.RunesChain
|
||||||
|
toToken:
|
||||||
|
| KnownTokenId.StacksToken
|
||||||
|
| KnownTokenId.EVMToken
|
||||||
|
| KnownTokenId.BRC20Token
|
||||||
|
| KnownTokenId.RunesToken
|
||||||
|
toAddress: string
|
||||||
|
toBitcoinScriptPubKey: Uint8Array
|
||||||
|
swap?: SwapRoute_WithMinimumAmountsToReceive
|
||||||
|
},
|
||||||
|
): Promise<undefined | CreateBridgeOrderResult> {
|
||||||
|
if (KnownChainId.isStacksChain(info.toChain)) {
|
||||||
|
if (KnownTokenId.isStacksToken(info.toToken)) {
|
||||||
|
return createBridgeOrder_BitcoinToStacks(sdkContext, {
|
||||||
|
...info,
|
||||||
|
toChain: info.toChain,
|
||||||
|
toToken: info.toToken,
|
||||||
|
toStacksAddress: info.toAddress,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertExclude(info.toChain, assertExclude.i<KnownChainId.StacksChain>())
|
||||||
|
|
||||||
|
if (KnownChainId.isEVMChain(info.toChain)) {
|
||||||
|
if (KnownTokenId.isEVMToken(info.toToken)) {
|
||||||
|
return createBridgeOrder_BitcoinToEVM(sdkContext, {
|
||||||
|
...info,
|
||||||
|
toChain: info.toChain,
|
||||||
|
toToken: info.toToken,
|
||||||
|
toEVMAddress: info.toAddress,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertExclude(info.toChain, assertExclude.i<KnownChainId.EVMChain>())
|
||||||
|
|
||||||
|
if (KnownChainId.isBRC20Chain(info.toChain)) {
|
||||||
|
if (KnownTokenId.isBRC20Token(info.toToken)) {
|
||||||
|
return createBridgeOrder_BitcoinToMeta(sdkContext, {
|
||||||
|
...info,
|
||||||
|
toChain: info.toChain,
|
||||||
|
toToken: info.toToken,
|
||||||
|
toBitcoinScriptPubKey: info.toBitcoinScriptPubKey,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertExclude(info.toChain, assertExclude.i<KnownChainId.BRC20Chain>())
|
||||||
|
|
||||||
|
if (KnownChainId.isRunesChain(info.toChain)) {
|
||||||
|
if (KnownTokenId.isRunesToken(info.toToken)) {
|
||||||
|
return createBridgeOrder_BitcoinToMeta(sdkContext, {
|
||||||
|
...info,
|
||||||
|
toChain: info.toChain,
|
||||||
|
toToken: info.toToken,
|
||||||
|
toBitcoinScriptPubKey: info.toBitcoinScriptPubKey,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertExclude(info.toChain, assertExclude.i<KnownChainId.RunesChain>())
|
||||||
|
|
||||||
|
checkNever(info.toChain)
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
KnownTokenId.Bitcoin.BTC,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createBridgeOrder_BitcoinToStacks(
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
|
info: {
|
||||||
|
fromChain: KnownChainId.BitcoinChain
|
||||||
|
fromBitcoinScriptPubKey: Uint8Array
|
||||||
|
toChain: KnownChainId.StacksChain
|
||||||
|
toToken: KnownTokenId.StacksToken
|
||||||
|
toStacksAddress: string
|
||||||
|
swap?: SwapRoute_WithMinimumAmountsToReceive
|
||||||
|
},
|
||||||
|
): Promise<undefined | CreateBridgeOrderResult> {
|
||||||
|
let data: undefined | Uint8Array
|
||||||
|
|
||||||
|
const contractBaseCallInfo = getStacksContractCallInfo(
|
||||||
|
info.toChain,
|
||||||
|
StacksContractName.BTCPegInEndpoint,
|
||||||
|
)
|
||||||
|
const contractSwapCallInfo = getStacksContractCallInfo(
|
||||||
|
info.toChain,
|
||||||
|
StacksContractName.BTCPegInEndpointSwap,
|
||||||
|
)
|
||||||
|
if (contractBaseCallInfo == null || contractSwapCallInfo == null) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
KnownTokenId.Bitcoin.BTC,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { toStacksAddress, swap: swapInfo } = info
|
||||||
|
|
||||||
|
const targetTokenContractInfo = await getStacksTokenContractInfo(
|
||||||
|
sdkContext,
|
||||||
|
info.toChain,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
if (targetTokenContractInfo == null) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swapInfo == null) {
|
||||||
|
data = await executeReadonlyCallXLINK(
|
||||||
|
contractBaseCallInfo.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}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
contractBaseCallInfo.executeOptions,
|
||||||
|
).then(unwrapResponse)
|
||||||
|
} else {
|
||||||
|
data = await executeReadonlyCallXLINK(
|
||||||
|
contractSwapCallInfo.contractName,
|
||||||
|
"create-order-cross-swap-or-fail",
|
||||||
|
{
|
||||||
|
order: {
|
||||||
|
from: info.fromBitcoinScriptPubKey,
|
||||||
|
to: addressToBuffer(info.toChain, toStacksAddress),
|
||||||
|
"chain-id": undefined,
|
||||||
|
routing: swapInfo.swapPools.map(n => n.poolId),
|
||||||
|
"min-amount-out": numberToStacksContractNumber(
|
||||||
|
swapInfo.minimumAmountsToReceive,
|
||||||
|
),
|
||||||
|
"token-out": `${targetTokenContractInfo.deployerAddress}.${targetTokenContractInfo.contractName}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
contractSwapCallInfo.executeOptions,
|
||||||
|
).then(unwrapResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
terminatingStacksToken: targetTokenContractInfo,
|
||||||
|
data: data!,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createBridgeOrder_BitcoinToEVM(
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
|
info: {
|
||||||
|
fromChain: KnownChainId.BitcoinChain
|
||||||
|
fromBitcoinScriptPubKey: Uint8Array
|
||||||
|
toChain: KnownChainId.EVMChain
|
||||||
|
toToken: KnownTokenId.EVMToken
|
||||||
|
toEVMAddress: string
|
||||||
|
swap?: SwapRoute_WithMinimumAmountsToReceive
|
||||||
|
},
|
||||||
|
): Promise<undefined | CreateBridgeOrderResult> {
|
||||||
|
const contractBaseCallInfo = getStacksContractCallInfo(
|
||||||
|
info.fromChain === KnownChainId.Bitcoin.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
StacksContractName.BTCPegInEndpoint,
|
||||||
|
)
|
||||||
|
const contractSwapCallInfo = getStacksContractCallInfo(
|
||||||
|
info.fromChain === KnownChainId.Bitcoin.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
StacksContractName.BTCPegInEndpointSwap,
|
||||||
|
)
|
||||||
|
if (contractBaseCallInfo == null || contractSwapCallInfo == null) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
KnownTokenId.Bitcoin.BTC,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { swap: swapInfo, toEVMAddress } = info
|
||||||
|
|
||||||
|
const targetChainId = contractAssignedChainIdFromKnownChain(info.toChain)
|
||||||
|
|
||||||
|
const swappedStacksToken = await evmTokenToCorrespondingStacksToken(
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
if (swappedStacksToken == null) return undefined
|
||||||
|
const swappedStacksTokenAddress = await getStacksTokenContractInfo(
|
||||||
|
sdkContext,
|
||||||
|
contractBaseCallInfo.network.isMainnet()
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
swappedStacksToken,
|
||||||
|
)
|
||||||
|
if (swappedStacksTokenAddress == null) return undefined
|
||||||
|
|
||||||
|
const terminatingStacksTokenAddress =
|
||||||
|
getTerminatingStacksTokenContractAddress({
|
||||||
|
fromChain: contractBaseCallInfo.network.isMainnet()
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
fromToken: swappedStacksToken,
|
||||||
|
toChain: info.toChain,
|
||||||
|
toToken: info.toToken,
|
||||||
|
}) ?? swappedStacksTokenAddress
|
||||||
|
|
||||||
|
let data: undefined | Uint8Array
|
||||||
|
if (swapInfo == null) {
|
||||||
|
data = await executeReadonlyCallXLINK(
|
||||||
|
contractBaseCallInfo.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}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
contractBaseCallInfo.executeOptions,
|
||||||
|
).then(unwrapResponse)
|
||||||
|
} else {
|
||||||
|
data = await executeReadonlyCallXLINK(
|
||||||
|
contractSwapCallInfo.contractName,
|
||||||
|
"create-order-cross-swap-or-fail",
|
||||||
|
{
|
||||||
|
order: {
|
||||||
|
from: info.fromBitcoinScriptPubKey,
|
||||||
|
to: decodeHex(toEVMAddress),
|
||||||
|
"chain-id": targetChainId,
|
||||||
|
routing: swapInfo.swapPools.map(n => n.poolId),
|
||||||
|
"min-amount-out": numberToStacksContractNumber(
|
||||||
|
swapInfo.minimumAmountsToReceive,
|
||||||
|
),
|
||||||
|
"token-out": `${terminatingStacksTokenAddress.deployerAddress}.${terminatingStacksTokenAddress.contractName}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
contractSwapCallInfo.executeOptions,
|
||||||
|
).then(unwrapResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
terminatingStacksToken: {
|
||||||
|
deployerAddress: terminatingStacksTokenAddress.deployerAddress,
|
||||||
|
contractName: terminatingStacksTokenAddress.contractName,
|
||||||
|
},
|
||||||
|
data: data!,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createBridgeOrder_BitcoinToMeta(
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
|
info: Omit<
|
||||||
|
KnownRoute_FromBitcoin_ToBRC20 | KnownRoute_FromBitcoin_ToRunes,
|
||||||
|
"fromToken"
|
||||||
|
> & {
|
||||||
|
fromBitcoinScriptPubKey: Uint8Array
|
||||||
|
toBitcoinScriptPubKey: Uint8Array
|
||||||
|
swap?: SwapRoute_WithMinimumAmountsToReceive
|
||||||
|
},
|
||||||
|
): Promise<undefined | CreateBridgeOrderResult> {
|
||||||
|
const contractBaseCallInfo = getStacksContractCallInfo(
|
||||||
|
info.fromChain === KnownChainId.Bitcoin.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
StacksContractName.BTCPegInEndpoint,
|
||||||
|
)
|
||||||
|
const contractSwapCallInfo = getStacksContractCallInfo(
|
||||||
|
info.fromChain === KnownChainId.Bitcoin.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
StacksContractName.BTCPegInEndpointSwap,
|
||||||
|
)
|
||||||
|
if (contractBaseCallInfo == null || contractSwapCallInfo == null) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
KnownTokenId.Bitcoin.BTC,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { swap: swapInfo } = info
|
||||||
|
|
||||||
|
const targetChainId = contractAssignedChainIdFromKnownChain(info.toChain)
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
const swappedStacksToken =
|
||||||
|
KnownChainId.isBRC20Chain(info.toChain) ?
|
||||||
|
await getBRC20SupportedRoutes(sdkContext, info.toChain).then(routes => {
|
||||||
|
const route = routes.find(n => n.brc20Token === info.toToken)
|
||||||
|
return route?.stacksToken
|
||||||
|
}) :
|
||||||
|
KnownChainId.isRunesChain(info.toChain) ?
|
||||||
|
await getRunesSupportedRoutes(sdkContext, info.toChain).then(routes => {
|
||||||
|
const route = routes.find(n => n.runesToken === info.toToken)
|
||||||
|
return route?.stacksToken
|
||||||
|
}) :
|
||||||
|
(checkNever(info.toChain), undefined)
|
||||||
|
if (swappedStacksToken == null) return undefined
|
||||||
|
const swappedStacksTokenAddress = await getStacksTokenContractInfo(
|
||||||
|
sdkContext,
|
||||||
|
contractBaseCallInfo.network.isMainnet()
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
swappedStacksToken,
|
||||||
|
)
|
||||||
|
if (swappedStacksTokenAddress == null) return undefined
|
||||||
|
|
||||||
|
let data: undefined | Uint8Array
|
||||||
|
if (swapInfo == null) {
|
||||||
|
data = await executeReadonlyCallXLINK(
|
||||||
|
contractBaseCallInfo.contractName,
|
||||||
|
"create-order-cross-or-fail",
|
||||||
|
{
|
||||||
|
order: {
|
||||||
|
from: info.fromBitcoinScriptPubKey,
|
||||||
|
to: info.toBitcoinScriptPubKey,
|
||||||
|
"chain-id": targetChainId,
|
||||||
|
token: `${swappedStacksTokenAddress.deployerAddress}.${swappedStacksTokenAddress.contractName}`,
|
||||||
|
"token-out": `${swappedStacksTokenAddress.deployerAddress}.${swappedStacksTokenAddress.contractName}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
contractBaseCallInfo.executeOptions,
|
||||||
|
).then(unwrapResponse)
|
||||||
|
} else {
|
||||||
|
data = await executeReadonlyCallXLINK(
|
||||||
|
contractSwapCallInfo.contractName,
|
||||||
|
"create-order-cross-swap-or-fail",
|
||||||
|
{
|
||||||
|
order: {
|
||||||
|
from: info.fromBitcoinScriptPubKey,
|
||||||
|
to: info.toBitcoinScriptPubKey,
|
||||||
|
"chain-id": targetChainId,
|
||||||
|
routing: swapInfo.swapPools.map(n => n.poolId),
|
||||||
|
"min-amount-out": numberToStacksContractNumber(
|
||||||
|
swapInfo.minimumAmountsToReceive,
|
||||||
|
),
|
||||||
|
"token-out": `${swappedStacksTokenAddress.deployerAddress}.${swappedStacksTokenAddress.contractName}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
contractSwapCallInfo.executeOptions,
|
||||||
|
).then(unwrapResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
terminatingStacksToken: {
|
||||||
|
deployerAddress: swappedStacksTokenAddress.deployerAddress,
|
||||||
|
contractName: swappedStacksTokenAddress.contractName,
|
||||||
|
},
|
||||||
|
data: data!,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { fromCorrespondingStacksToken } from "../evmUtils/peggingHelpers"
|
import { evmTokenFromCorrespondingStacksToken } from "../evmUtils/peggingHelpers"
|
||||||
import { getEVMTokenContractInfo } from "../evmUtils/xlinkContractHelpers"
|
import { getEVMTokenContractInfo } from "../evmUtils/xlinkContractHelpers"
|
||||||
import {
|
import {
|
||||||
getBRC20SupportedRoutes,
|
getBRC20SupportedRoutes,
|
||||||
@@ -82,7 +82,10 @@ export const isSupportedStacksRoute: IsSupportedFn = async (ctx, route) => {
|
|||||||
const info = await getEVMTokenContractInfo(ctx, toChain, toToken)
|
const info = await getEVMTokenContractInfo(ctx, toChain, toToken)
|
||||||
if (info == null) return false
|
if (info == null) return false
|
||||||
|
|
||||||
const toEVMTokens = await fromCorrespondingStacksToken(toChain, fromToken)
|
const toEVMTokens = await evmTokenFromCorrespondingStacksToken(
|
||||||
|
toChain,
|
||||||
|
fromToken,
|
||||||
|
)
|
||||||
return toEVMTokens.includes(toToken)
|
return toEVMTokens.includes(toToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { contractNameOverrides } from "../config"
|
||||||
import { KnownRoute_FromStacks_ToEVM } from "../utils/buildSupportedRoutes"
|
import { KnownRoute_FromStacks_ToEVM } from "../utils/buildSupportedRoutes"
|
||||||
import { assertExclude, checkNever } from "../utils/typeHelpers"
|
import { assertExclude, checkNever } from "../utils/typeHelpers"
|
||||||
import { KnownChainId, KnownTokenId } from "../utils/types/knownIds"
|
import { KnownChainId, KnownTokenId } from "../utils/types/knownIds"
|
||||||
@@ -21,51 +22,281 @@ export const alexContractDeployerMainnet =
|
|||||||
export const alexContractDeployerTestnet =
|
export const alexContractDeployerTestnet =
|
||||||
"ST1J4G6RR643BCG8G8SR6M2D9Z9KXT2NJDRK3FBTK"
|
"ST1J4G6RR643BCG8G8SR6M2D9Z9KXT2NJDRK3FBTK"
|
||||||
|
|
||||||
|
export const alexContractMultisigMainnet =
|
||||||
|
"SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC"
|
||||||
|
export const alexContractMultisigTestnet =
|
||||||
|
"ST1J4G6RR643BCG8G8SR6M2D9Z9KXT2NJDRK3FBTK"
|
||||||
|
|
||||||
export const legacyAlexContractDeployerMainnet =
|
export const legacyAlexContractDeployerMainnet =
|
||||||
"SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9"
|
"SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9"
|
||||||
export const legacyAlexContractDeployerTestnet =
|
export const legacyAlexContractDeployerTestnet =
|
||||||
"ST1J2JTYXGRMZYNKE40GM87ZCACSPSSEEQVSNB7DC"
|
"ST1J2JTYXGRMZYNKE40GM87ZCACSPSSEEQVSNB7DC"
|
||||||
|
|
||||||
const stxAlternativeTokenContractAddresses = {
|
const wrapContractAddress = (
|
||||||
|
address: StacksContractAddress,
|
||||||
|
): StacksContractAddress => {
|
||||||
|
const contractName =
|
||||||
|
(contractNameOverrides as any)?.[address.contractName] ??
|
||||||
|
address.contractName
|
||||||
|
return {
|
||||||
|
...address,
|
||||||
|
contractName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum StacksContractName {
|
||||||
|
BTCPegInEndpoint = "btc-peg-in-endpoint-v2-05",
|
||||||
|
BTCPegInEndpointSwap = "btc-peg-in-endpoint-v2-07-swap",
|
||||||
|
BTCPegOutEndpoint = "btc-peg-out-endpoint-v2-01",
|
||||||
|
MetaPegInEndpoint = "meta-peg-in-endpoint-v2-04",
|
||||||
|
MetaPegInEndpointSwap = "meta-peg-in-endpoint-v2-06-swap",
|
||||||
|
MetaPegOutEndpoint = "meta-peg-out-endpoint-v2-04",
|
||||||
|
EVMPegInEndpoint = "cross-peg-in-endpoint-v2-04",
|
||||||
|
EVMPegOutEndpoint = "cross-peg-out-endpoint-v2-01",
|
||||||
|
}
|
||||||
|
|
||||||
|
export const stxContractAddresses = {
|
||||||
|
[StacksContractName.BTCPegInEndpoint]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsMultisigMainnet,
|
||||||
|
contractName: StacksContractName.BTCPegInEndpoint,
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsMultisigTestnet,
|
||||||
|
contractName: StacksContractName.BTCPegInEndpoint,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[StacksContractName.BTCPegInEndpointSwap]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsMultisigMainnet,
|
||||||
|
contractName: StacksContractName.BTCPegInEndpointSwap,
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsMultisigTestnet,
|
||||||
|
contractName: StacksContractName.BTCPegInEndpointSwap,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[StacksContractName.BTCPegOutEndpoint]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsDeployerMainnet,
|
||||||
|
contractName: StacksContractName.BTCPegOutEndpoint,
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsDeployerTestnet,
|
||||||
|
contractName: StacksContractName.BTCPegOutEndpoint,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[StacksContractName.EVMPegInEndpoint]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsMultisigMainnet,
|
||||||
|
contractName: StacksContractName.EVMPegInEndpoint,
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsMultisigTestnet,
|
||||||
|
contractName: StacksContractName.EVMPegInEndpoint,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[StacksContractName.EVMPegOutEndpoint]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsDeployerMainnet,
|
||||||
|
contractName: StacksContractName.EVMPegOutEndpoint,
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsDeployerTestnet,
|
||||||
|
contractName: StacksContractName.EVMPegOutEndpoint,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[StacksContractName.MetaPegInEndpoint]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsMultisigMainnet,
|
||||||
|
contractName: StacksContractName.MetaPegInEndpoint,
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsMultisigTestnet,
|
||||||
|
contractName: StacksContractName.MetaPegInEndpoint,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[StacksContractName.MetaPegInEndpointSwap]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsMultisigMainnet,
|
||||||
|
contractName: StacksContractName.MetaPegInEndpointSwap,
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsMultisigTestnet,
|
||||||
|
contractName: StacksContractName.MetaPegInEndpointSwap,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[StacksContractName.MetaPegOutEndpoint]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsMultisigMainnet,
|
||||||
|
contractName: StacksContractName.MetaPegOutEndpoint,
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsMultisigTestnet,
|
||||||
|
contractName: StacksContractName.MetaPegOutEndpoint,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
} satisfies Record<
|
||||||
|
StacksContractName,
|
||||||
|
Record<KnownChainId.StacksChain, StacksContractAddress>
|
||||||
|
>
|
||||||
|
|
||||||
|
export const stxTokenContractAddresses: Record<
|
||||||
|
string,
|
||||||
|
Record<KnownChainId.StacksChain, StacksContractAddress>
|
||||||
|
> = {
|
||||||
|
[KnownTokenId.Stacks.ALEX]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: alexContractDeployerMainnet,
|
||||||
|
contractName: "token-alex",
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: alexContractDeployerTestnet,
|
||||||
|
contractName: "token-alex",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[KnownTokenId.Stacks.aBTC]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsDeployerMainnet,
|
||||||
|
contractName: "token-abtc",
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsDeployerTestnet,
|
||||||
|
contractName: "token-abtc",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[KnownTokenId.Stacks.sUSDT]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsDeployerMainnet,
|
||||||
|
contractName: "token-susdt",
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsDeployerTestnet,
|
||||||
|
contractName: "token-susdt",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[KnownTokenId.Stacks.sLUNR]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsDeployerMainnet,
|
||||||
|
contractName: "token-slunr",
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsDeployerTestnet,
|
||||||
|
contractName: "token-slunr",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[KnownTokenId.Stacks.sSKO]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsDeployerMainnet,
|
||||||
|
contractName: "token-ssko",
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsDeployerTestnet,
|
||||||
|
contractName: "token-ssko",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[KnownTokenId.Stacks.vLiSTX]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: alexContractDeployerMainnet,
|
||||||
|
contractName: "token-wvlqstx",
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: alexContractDeployerTestnet,
|
||||||
|
contractName: "token-wvlqstx",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[KnownTokenId.Stacks.vLiALEX]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: alexContractDeployerMainnet,
|
||||||
|
contractName: "token-wvlialex",
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: alexContractDeployerTestnet,
|
||||||
|
contractName: "token-wvlialex",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[KnownTokenId.Stacks.vLiaBTC]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: alexContractMultisigMainnet,
|
||||||
|
contractName: "token-wvliabtc",
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: alexContractDeployerTestnet,
|
||||||
|
contractName: "token-wvliabtc",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[KnownTokenId.Stacks.uBTC]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsMultisigMainnet,
|
||||||
|
contractName: "token-ubtc",
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: xlinkContractsDeployerTestnet,
|
||||||
|
contractName: "token-ubtc",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[KnownTokenId.Stacks.DB20]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: legacyAlexContractDeployerMainnet,
|
||||||
|
contractName: "brc20-db20",
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: legacyAlexContractDeployerTestnet,
|
||||||
|
contractName: "brc20-db20",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[KnownTokenId.Stacks.DOG]: {
|
||||||
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
|
deployerAddress: legacyAlexContractDeployerMainnet,
|
||||||
|
contractName: "runes-dog",
|
||||||
|
}),
|
||||||
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
|
deployerAddress: legacyAlexContractDeployerTestnet,
|
||||||
|
contractName: "runes-dog",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const terminatingStacksTokenContractAddresses = {
|
||||||
wbtc: {
|
wbtc: {
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
deployerAddress: xlinkContractsMultisigMainnet,
|
deployerAddress: xlinkContractsMultisigMainnet,
|
||||||
contractName: "token-wbtc",
|
contractName: "token-wbtc",
|
||||||
},
|
}),
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
deployerAddress: xlinkContractsDeployerTestnet,
|
deployerAddress: xlinkContractsDeployerTestnet,
|
||||||
contractName: "token-wbtc",
|
contractName: "token-wbtc",
|
||||||
},
|
}),
|
||||||
},
|
},
|
||||||
btcb: {
|
btcb: {
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
deployerAddress: xlinkContractsMultisigMainnet,
|
deployerAddress: xlinkContractsMultisigMainnet,
|
||||||
contractName: "token-wbtc",
|
contractName: "token-wbtc",
|
||||||
},
|
}),
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
deployerAddress: xlinkContractsDeployerTestnet,
|
deployerAddress: xlinkContractsDeployerTestnet,
|
||||||
contractName: "token-btcb",
|
contractName: "token-btcb",
|
||||||
},
|
}),
|
||||||
},
|
},
|
||||||
cbBTC: {
|
cbBTC: {
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
deployerAddress: xlinkContractsMultisigMainnet,
|
deployerAddress: xlinkContractsMultisigMainnet,
|
||||||
contractName: "token-wbtc",
|
contractName: "token-wbtc",
|
||||||
},
|
}),
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
deployerAddress: xlinkContractsDeployerTestnet,
|
deployerAddress: xlinkContractsDeployerTestnet,
|
||||||
contractName: "token-wbtc",
|
contractName: "token-wbtc",
|
||||||
},
|
}),
|
||||||
},
|
},
|
||||||
usdt: {
|
usdt: {
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
[KnownChainId.Stacks.Mainnet]: wrapContractAddress({
|
||||||
deployerAddress: xlinkContractsMultisigMainnet,
|
deployerAddress: xlinkContractsMultisigMainnet,
|
||||||
contractName: "token-usdt",
|
contractName: "token-usdt",
|
||||||
},
|
}),
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
[KnownChainId.Stacks.Testnet]: wrapContractAddress({
|
||||||
deployerAddress: xlinkContractsDeployerTestnet,
|
deployerAddress: xlinkContractsDeployerTestnet,
|
||||||
contractName: "token-usdt",
|
contractName: "token-usdt",
|
||||||
},
|
}),
|
||||||
},
|
},
|
||||||
usdc: {
|
usdc: {
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
[KnownChainId.Stacks.Mainnet]: {
|
||||||
@@ -93,7 +324,7 @@ export const getTerminatingStacksTokenContractAddress = (
|
|||||||
toChain === KnownChainId.EVM.Sepolia) &&
|
toChain === KnownChainId.EVM.Sepolia) &&
|
||||||
toToken === KnownTokenId.EVM.WBTC
|
toToken === KnownTokenId.EVM.WBTC
|
||||||
) {
|
) {
|
||||||
return stxAlternativeTokenContractAddresses.wbtc[route.fromChain]
|
return terminatingStacksTokenContractAddresses.wbtc[route.fromChain]
|
||||||
}
|
}
|
||||||
assertExclude(restChains, KnownChainId.EVM.Ethereum)
|
assertExclude(restChains, KnownChainId.EVM.Ethereum)
|
||||||
assertExclude(restChains, KnownChainId.EVM.Sepolia)
|
assertExclude(restChains, KnownChainId.EVM.Sepolia)
|
||||||
@@ -103,7 +334,7 @@ export const getTerminatingStacksTokenContractAddress = (
|
|||||||
toChain === KnownChainId.EVM.BSCTestnet) &&
|
toChain === KnownChainId.EVM.BSCTestnet) &&
|
||||||
toToken === KnownTokenId.EVM.BTCB
|
toToken === KnownTokenId.EVM.BTCB
|
||||||
) {
|
) {
|
||||||
return stxAlternativeTokenContractAddresses.btcb[route.fromChain]
|
return terminatingStacksTokenContractAddresses.btcb[route.fromChain]
|
||||||
}
|
}
|
||||||
assertExclude(restChains, KnownChainId.EVM.BSC)
|
assertExclude(restChains, KnownChainId.EVM.BSC)
|
||||||
assertExclude(restChains, KnownChainId.EVM.BSCTestnet)
|
assertExclude(restChains, KnownChainId.EVM.BSCTestnet)
|
||||||
@@ -112,7 +343,7 @@ export const getTerminatingStacksTokenContractAddress = (
|
|||||||
toChain === KnownChainId.EVM.Base &&
|
toChain === KnownChainId.EVM.Base &&
|
||||||
toToken === KnownTokenId.EVM.cbBTC
|
toToken === KnownTokenId.EVM.cbBTC
|
||||||
) {
|
) {
|
||||||
return stxAlternativeTokenContractAddresses.cbBTC[route.fromChain]
|
return terminatingStacksTokenContractAddresses.cbBTC[route.fromChain]
|
||||||
}
|
}
|
||||||
assertExclude(restChains, KnownChainId.EVM.Base)
|
assertExclude(restChains, KnownChainId.EVM.Base)
|
||||||
|
|
||||||
@@ -129,7 +360,7 @@ export const getTerminatingStacksTokenContractAddress = (
|
|||||||
toChain === KnownChainId.EVM.BSCTestnet) &&
|
toChain === KnownChainId.EVM.BSCTestnet) &&
|
||||||
toToken === KnownTokenId.EVM.USDT
|
toToken === KnownTokenId.EVM.USDT
|
||||||
) {
|
) {
|
||||||
return stxAlternativeTokenContractAddresses.usdt[route.fromChain]
|
return terminatingStacksTokenContractAddresses.usdt[route.fromChain]
|
||||||
}
|
}
|
||||||
assertExclude(restChains, KnownChainId.EVM.Ethereum)
|
assertExclude(restChains, KnownChainId.EVM.Ethereum)
|
||||||
assertExclude(restChains, KnownChainId.EVM.Sepolia)
|
assertExclude(restChains, KnownChainId.EVM.Sepolia)
|
||||||
@@ -140,7 +371,7 @@ export const getTerminatingStacksTokenContractAddress = (
|
|||||||
toChain === KnownChainId.EVM.Base &&
|
toChain === KnownChainId.EVM.Base &&
|
||||||
toToken === KnownTokenId.EVM.USDC
|
toToken === KnownTokenId.EVM.USDC
|
||||||
) {
|
) {
|
||||||
return stxAlternativeTokenContractAddresses.usdt[route.fromChain]
|
return terminatingStacksTokenContractAddresses.usdc[route.fromChain]
|
||||||
}
|
}
|
||||||
assertExclude(restChains, KnownChainId.EVM.Base)
|
assertExclude(restChains, KnownChainId.EVM.Base)
|
||||||
|
|
||||||
@@ -161,7 +392,7 @@ export const getEVMTokenIdFromTerminatingStacksTokenContractAddress = (route: {
|
|||||||
route.evmChain === KnownChainId.EVM.Sepolia) &&
|
route.evmChain === KnownChainId.EVM.Sepolia) &&
|
||||||
isStacksContractAddressEqual(
|
isStacksContractAddressEqual(
|
||||||
route.stacksTokenAddress,
|
route.stacksTokenAddress,
|
||||||
stxAlternativeTokenContractAddresses.wbtc[route.stacksChain],
|
terminatingStacksTokenContractAddresses.wbtc[route.stacksChain],
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return KnownTokenId.EVM.WBTC
|
return KnownTokenId.EVM.WBTC
|
||||||
@@ -174,7 +405,7 @@ export const getEVMTokenIdFromTerminatingStacksTokenContractAddress = (route: {
|
|||||||
route.evmChain === KnownChainId.EVM.BSCTestnet) &&
|
route.evmChain === KnownChainId.EVM.BSCTestnet) &&
|
||||||
isStacksContractAddressEqual(
|
isStacksContractAddressEqual(
|
||||||
route.stacksTokenAddress,
|
route.stacksTokenAddress,
|
||||||
stxAlternativeTokenContractAddresses.btcb[route.stacksChain],
|
terminatingStacksTokenContractAddresses.btcb[route.stacksChain],
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return KnownTokenId.EVM.BTCB
|
return KnownTokenId.EVM.BTCB
|
||||||
@@ -186,7 +417,7 @@ export const getEVMTokenIdFromTerminatingStacksTokenContractAddress = (route: {
|
|||||||
route.evmChain === KnownChainId.EVM.Base &&
|
route.evmChain === KnownChainId.EVM.Base &&
|
||||||
isStacksContractAddressEqual(
|
isStacksContractAddressEqual(
|
||||||
route.stacksTokenAddress,
|
route.stacksTokenAddress,
|
||||||
stxAlternativeTokenContractAddresses.cbBTC[route.stacksChain],
|
terminatingStacksTokenContractAddresses.cbBTC[route.stacksChain],
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return KnownTokenId.EVM.cbBTC
|
return KnownTokenId.EVM.cbBTC
|
||||||
@@ -200,7 +431,7 @@ export const getEVMTokenIdFromTerminatingStacksTokenContractAddress = (route: {
|
|||||||
route.evmChain === KnownChainId.EVM.BSCTestnet) &&
|
route.evmChain === KnownChainId.EVM.BSCTestnet) &&
|
||||||
isStacksContractAddressEqual(
|
isStacksContractAddressEqual(
|
||||||
route.stacksTokenAddress,
|
route.stacksTokenAddress,
|
||||||
stxAlternativeTokenContractAddresses.usdt[route.stacksChain],
|
terminatingStacksTokenContractAddresses.usdt[route.stacksChain],
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return KnownTokenId.EVM.USDT
|
return KnownTokenId.EVM.USDT
|
||||||
@@ -214,7 +445,7 @@ export const getEVMTokenIdFromTerminatingStacksTokenContractAddress = (route: {
|
|||||||
route.evmChain === KnownChainId.EVM.Base &&
|
route.evmChain === KnownChainId.EVM.Base &&
|
||||||
isStacksContractAddressEqual(
|
isStacksContractAddressEqual(
|
||||||
route.stacksTokenAddress,
|
route.stacksTokenAddress,
|
||||||
stxAlternativeTokenContractAddresses.usdc[route.stacksChain],
|
terminatingStacksTokenContractAddresses.usdc[route.stacksChain],
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return KnownTokenId.EVM.USDC
|
return KnownTokenId.EVM.USDC
|
||||||
@@ -237,163 +468,3 @@ type ChainsHaveAlternativeBTC =
|
|||||||
| typeof KnownChainId.EVM.BSC
|
| typeof KnownChainId.EVM.BSC
|
||||||
| typeof KnownChainId.EVM.BSCTestnet
|
| typeof KnownChainId.EVM.BSCTestnet
|
||||||
| typeof KnownChainId.EVM.Base
|
| typeof KnownChainId.EVM.Base
|
||||||
|
|
||||||
export const stxContractDeployers = {
|
|
||||||
"btc-peg-in-endpoint-v2-05": {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: xlinkContractsMultisigMainnet,
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: xlinkContractsMultisigTestnet,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"btc-peg-out-endpoint-v2-01": {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: xlinkContractsDeployerMainnet,
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: xlinkContractsDeployerTestnet,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"cross-peg-in-endpoint-v2-04": {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: xlinkContractsMultisigMainnet,
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: xlinkContractsMultisigTestnet,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"cross-peg-out-endpoint-v2-01": {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: xlinkContractsDeployerMainnet,
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: xlinkContractsDeployerTestnet,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"meta-peg-in-endpoint-v2-04": {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: xlinkContractsMultisigMainnet,
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: xlinkContractsMultisigTestnet,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"meta-peg-out-endpoint-v2-04": {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: xlinkContractsMultisigMainnet,
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: xlinkContractsMultisigTestnet,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} satisfies Record<
|
|
||||||
string,
|
|
||||||
Record<KnownChainId.StacksChain, { deployerAddress: string }>
|
|
||||||
>
|
|
||||||
|
|
||||||
export const stxTokenContractAddresses: Record<
|
|
||||||
KnownTokenId.StacksToken,
|
|
||||||
Record<KnownChainId.StacksChain, StacksContractAddress>
|
|
||||||
> = {
|
|
||||||
[KnownTokenId.Stacks.ALEX]: {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: alexContractDeployerMainnet,
|
|
||||||
contractName: "token-alex",
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: alexContractDeployerTestnet,
|
|
||||||
contractName: "token-alex",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[KnownTokenId.Stacks.aBTC]: {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: xlinkContractsDeployerMainnet,
|
|
||||||
contractName: "token-abtc",
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: xlinkContractsDeployerTestnet,
|
|
||||||
contractName: "token-abtc",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[KnownTokenId.Stacks.sUSDT]: {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: xlinkContractsDeployerMainnet,
|
|
||||||
contractName: "token-susdt",
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: xlinkContractsDeployerTestnet,
|
|
||||||
contractName: "token-susdt",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[KnownTokenId.Stacks.sLUNR]: {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: xlinkContractsDeployerMainnet,
|
|
||||||
contractName: "token-slunr",
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: xlinkContractsDeployerTestnet,
|
|
||||||
contractName: "token-slunr",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[KnownTokenId.Stacks.sSKO]: {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: xlinkContractsDeployerMainnet,
|
|
||||||
contractName: "token-ssko",
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: xlinkContractsDeployerTestnet,
|
|
||||||
contractName: "token-ssko",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[KnownTokenId.Stacks.vLiSTX]: {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: alexContractDeployerMainnet,
|
|
||||||
contractName: "token-wvlqstx",
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: alexContractDeployerTestnet,
|
|
||||||
contractName: "token-wvlqstx",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[KnownTokenId.Stacks.vLiALEX]: {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: alexContractDeployerMainnet,
|
|
||||||
contractName: "token-wvlialex",
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: alexContractDeployerTestnet,
|
|
||||||
contractName: "token-wvlialex",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[KnownTokenId.Stacks.uBTC]: {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: xlinkContractsMultisigMainnet,
|
|
||||||
contractName: "token-ubtc",
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: xlinkContractsDeployerTestnet,
|
|
||||||
contractName: "token-ubtc",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[KnownTokenId.Stacks.DB20]: {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: legacyAlexContractDeployerMainnet,
|
|
||||||
contractName: "brc20-db20",
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: legacyAlexContractDeployerTestnet,
|
|
||||||
contractName: "brc20-db20",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[KnownTokenId.Stacks.DOG]: {
|
|
||||||
[KnownChainId.Stacks.Mainnet]: {
|
|
||||||
deployerAddress: legacyAlexContractDeployerMainnet,
|
|
||||||
contractName: "runes-dog",
|
|
||||||
},
|
|
||||||
[KnownChainId.Stacks.Testnet]: {
|
|
||||||
deployerAddress: legacyAlexContractDeployerTestnet,
|
|
||||||
contractName: "runes-dog",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { callReadOnlyFunction } from "@stacks/transactions"
|
import { Response } from "clarity-codegen"
|
||||||
import { CallReadOnlyFunctionFn, Response } from "clarity-codegen"
|
|
||||||
import { hasLength } from "../utils/arrayHelpers"
|
import { hasLength } from "../utils/arrayHelpers"
|
||||||
import { checkNever } from "../utils/typeHelpers"
|
import { SwapRoute } from "../utils/SwapRouteHelpers"
|
||||||
import { KnownChainId } from "../utils/types/knownIds"
|
import { KnownChainId } from "../utils/types/knownIds"
|
||||||
import { StacksContractAddress } from "../xlinkSdkUtils/types"
|
import { StacksContractAddress } from "../xlinkSdkUtils/types"
|
||||||
import { BridgeSwapRoute_FromBitcoin } from "./createBridgeOrder"
|
import { StacksContractName } from "./stxContractAddresses"
|
||||||
import {
|
import {
|
||||||
executeReadonlyCallXLINK,
|
executeReadonlyCallXLINK,
|
||||||
getStacksContractCallInfo,
|
getStacksContractCallInfo,
|
||||||
@@ -15,35 +14,33 @@ export async function validateBridgeOrder(info: {
|
|||||||
commitTx: Uint8Array
|
commitTx: Uint8Array
|
||||||
revealTx: Uint8Array
|
revealTx: Uint8Array
|
||||||
terminatingStacksToken: StacksContractAddress
|
terminatingStacksToken: StacksContractAddress
|
||||||
swapRoute: BridgeSwapRoute_FromBitcoin
|
swapRoute?: SwapRoute
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const contractCallInfo = getStacksContractCallInfo(
|
const contractBaseCallInfo = getStacksContractCallInfo(
|
||||||
info.chainId === KnownChainId.Bitcoin.Mainnet
|
info.chainId === KnownChainId.Bitcoin.Mainnet
|
||||||
? KnownChainId.Stacks.Mainnet
|
? KnownChainId.Stacks.Mainnet
|
||||||
: KnownChainId.Stacks.Testnet,
|
: KnownChainId.Stacks.Testnet,
|
||||||
"btc-peg-in-endpoint-v2-05",
|
StacksContractName.BTCPegInEndpoint,
|
||||||
)
|
)
|
||||||
if (contractCallInfo == null) {
|
const contractSwapCallInfo = getStacksContractCallInfo(
|
||||||
|
info.chainId === KnownChainId.Bitcoin.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet,
|
||||||
|
StacksContractName.BTCPegInEndpointSwap,
|
||||||
|
)
|
||||||
|
if (contractBaseCallInfo == null || contractSwapCallInfo == null) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"[validateBridgeOrder_BitcoinToEVM] stacks contract information not found",
|
"[validateBridgeOrder_BitcoinToEVM] stacks contract information not found",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { commitTx, revealTx, swapRoute } = info
|
const { commitTx, revealTx, swapRoute } = info
|
||||||
const executeOptions = {
|
|
||||||
deployerAddress: contractCallInfo.deployerAddress,
|
|
||||||
callReadOnlyFunction: (callOptions =>
|
|
||||||
callReadOnlyFunction({
|
|
||||||
...callOptions,
|
|
||||||
network: contractCallInfo.network,
|
|
||||||
})) satisfies CallReadOnlyFunctionFn,
|
|
||||||
}
|
|
||||||
|
|
||||||
let resp: Response<any>
|
let resp: Response<any>
|
||||||
|
|
||||||
if (hasLength(swapRoute, 0)) {
|
if (swapRoute == null || hasLength(swapRoute.swapPools, 0)) {
|
||||||
resp = await executeReadonlyCallXLINK(
|
resp = await executeReadonlyCallXLINK(
|
||||||
contractCallInfo.contractName,
|
contractBaseCallInfo.contractName,
|
||||||
"validate-tx-cross",
|
"validate-tx-cross",
|
||||||
{
|
{
|
||||||
"commit-tx": {
|
"commit-tx": {
|
||||||
@@ -56,10 +53,33 @@ export async function validateBridgeOrder(info: {
|
|||||||
},
|
},
|
||||||
"token-out-trait": `${info.terminatingStacksToken.deployerAddress}.${info.terminatingStacksToken.contractName}`,
|
"token-out-trait": `${info.terminatingStacksToken.deployerAddress}.${info.terminatingStacksToken.contractName}`,
|
||||||
},
|
},
|
||||||
executeOptions,
|
contractBaseCallInfo.executeOptions,
|
||||||
|
)
|
||||||
|
} else if (swapRoute.swapPools.length < 4) {
|
||||||
|
resp = await executeReadonlyCallXLINK(
|
||||||
|
contractSwapCallInfo.contractName,
|
||||||
|
"validate-tx-cross-swap",
|
||||||
|
{
|
||||||
|
"commit-tx": {
|
||||||
|
tx: commitTx,
|
||||||
|
"output-idx": 1n,
|
||||||
|
},
|
||||||
|
"reveal-tx": {
|
||||||
|
tx: revealTx,
|
||||||
|
"order-idx": 0n,
|
||||||
|
},
|
||||||
|
"token-out-trait": `${info.terminatingStacksToken.deployerAddress}.${info.terminatingStacksToken.contractName}`,
|
||||||
|
"routing-traits": [
|
||||||
|
`${swapRoute.fromTokenAddress.deployerAddress}.${swapRoute.fromTokenAddress.contractName}`,
|
||||||
|
...swapRoute.swapPools.map(
|
||||||
|
(pool): `${string}.${string}` =>
|
||||||
|
`${pool.toTokenAddress.deployerAddress}.${pool.toTokenAddress.contractName}`,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
contractSwapCallInfo.executeOptions,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
checkNever(swapRoute)
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`[validateBridgeOrder_BitcoinToEVM] unsupported swap route length: ${(swapRoute as any).length}`,
|
`[validateBridgeOrder_BitcoinToEVM] unsupported swap route length: ${(swapRoute as any).length}`,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,32 +1,27 @@
|
|||||||
import { NETWORK, TEST_NETWORK } from "@scure/btc-signer"
|
|
||||||
import { StacksNetwork } from "@stacks/network"
|
import { StacksNetwork } from "@stacks/network"
|
||||||
|
import { callReadOnlyFunction } from "@stacks/transactions"
|
||||||
import {
|
import {
|
||||||
c32address,
|
CallReadOnlyFunctionFn,
|
||||||
c32addressDecode,
|
|
||||||
versions as c32addressVersions,
|
|
||||||
} from "c32check"
|
|
||||||
import {
|
|
||||||
composeTxOptionsFactory,
|
composeTxOptionsFactory,
|
||||||
executeReadonlyCallFactory,
|
executeReadonlyCallFactory,
|
||||||
} from "clarity-codegen"
|
} from "clarity-codegen"
|
||||||
import { xlinkContracts } from "../../generated/smartContract/contracts_xlink"
|
import { xlinkContracts } from "../../generated/smartContract/contracts_xlink"
|
||||||
import {
|
|
||||||
addressToScriptPubKey,
|
|
||||||
scriptPubKeyToAddress,
|
|
||||||
} from "../bitcoinUtils/bitcoinHelpers"
|
|
||||||
import { STACKS_MAINNET, STACKS_TESTNET } from "../config"
|
import { STACKS_MAINNET, STACKS_TESTNET } from "../config"
|
||||||
|
import { requestAPI } from "../utils/apiHelpers"
|
||||||
import { BigNumber, BigNumberSource } from "../utils/BigNumber"
|
import { BigNumber, BigNumberSource } from "../utils/BigNumber"
|
||||||
import { StacksAddressVersionNotSupportedError } from "../utils/errors"
|
|
||||||
import {
|
import {
|
||||||
decodeHex,
|
createStacksToken,
|
||||||
encodeHex,
|
KnownChainId,
|
||||||
encodeZeroPrefixedHex,
|
KnownTokenId,
|
||||||
} from "../utils/hexHelpers"
|
} from "../utils/types/knownIds"
|
||||||
import { checkNever } from "../utils/typeHelpers"
|
|
||||||
import { KnownChainId, KnownTokenId } from "../utils/types/knownIds"
|
|
||||||
import { StacksContractAddress } from "../xlinkSdkUtils/types"
|
|
||||||
import {
|
import {
|
||||||
stxContractDeployers,
|
isStacksContractAddressEqual,
|
||||||
|
StacksContractAddress,
|
||||||
|
} from "../xlinkSdkUtils/types"
|
||||||
|
import { SDKGlobalContext } from "../xlinkSdkUtils/types.internal"
|
||||||
|
import {
|
||||||
|
StacksContractName,
|
||||||
|
stxContractAddresses,
|
||||||
stxTokenContractAddresses,
|
stxTokenContractAddresses,
|
||||||
} from "./stxContractAddresses"
|
} from "./stxContractAddresses"
|
||||||
|
|
||||||
@@ -50,6 +45,16 @@ export const numberToStacksContractNumber = (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const _composeTxXLINK = composeTxOptionsFactory(xlinkContracts, {})
|
||||||
|
// export const composeTxXLINK: typeof _composeTxXLINK = (...args) => {
|
||||||
|
// const res = _composeTxXLINK(...args)
|
||||||
|
// return {
|
||||||
|
// ...res,
|
||||||
|
// contractName:
|
||||||
|
// (contractNameOverrides as any)?.[res.contractName] ?? res.contractName,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
export const composeTxXLINK = composeTxOptionsFactory(xlinkContracts, {})
|
export const composeTxXLINK = composeTxOptionsFactory(xlinkContracts, {})
|
||||||
|
|
||||||
export const executeReadonlyCallXLINK = executeReadonlyCallFactory(
|
export const executeReadonlyCallXLINK = executeReadonlyCallFactory(
|
||||||
@@ -57,9 +62,7 @@ export const executeReadonlyCallXLINK = executeReadonlyCallFactory(
|
|||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
|
|
||||||
export const getStacksContractCallInfo = <
|
export const getStacksContractCallInfo = <C extends StacksContractName>(
|
||||||
C extends keyof typeof stxContractDeployers,
|
|
||||||
>(
|
|
||||||
chainId: KnownChainId.StacksChain,
|
chainId: KnownChainId.StacksChain,
|
||||||
contractName: C,
|
contractName: C,
|
||||||
):
|
):
|
||||||
@@ -67,39 +70,72 @@ export const getStacksContractCallInfo = <
|
|||||||
| (Omit<StacksContractAddress, "contractName"> & {
|
| (Omit<StacksContractAddress, "contractName"> & {
|
||||||
contractName: C
|
contractName: C
|
||||||
network: StacksNetwork
|
network: StacksNetwork
|
||||||
|
executeOptions: {
|
||||||
|
deployerAddress?: string
|
||||||
|
senderAddress?: string
|
||||||
|
callReadOnlyFunction?: CallReadOnlyFunctionFn
|
||||||
|
}
|
||||||
}) => {
|
}) => {
|
||||||
const network =
|
const network =
|
||||||
chainId === KnownChainId.Stacks.Mainnet ? STACKS_MAINNET : STACKS_TESTNET
|
chainId === KnownChainId.Stacks.Mainnet ? STACKS_MAINNET : STACKS_TESTNET
|
||||||
|
|
||||||
if (stxContractDeployers[contractName][chainId] == null) {
|
if (stxContractAddresses[contractName][chainId] == null) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...stxContractDeployers[contractName][chainId],
|
...stxContractAddresses[contractName][chainId],
|
||||||
contractName,
|
contractName,
|
||||||
network,
|
network,
|
||||||
|
executeOptions: {
|
||||||
|
deployerAddress:
|
||||||
|
stxContractAddresses[contractName][chainId].deployerAddress,
|
||||||
|
callReadOnlyFunction(callOptions) {
|
||||||
|
return callReadOnlyFunction({
|
||||||
|
...callOptions,
|
||||||
|
contractAddress:
|
||||||
|
stxContractAddresses[contractName][chainId].deployerAddress,
|
||||||
|
contractName:
|
||||||
|
stxContractAddresses[contractName][chainId].contractName,
|
||||||
|
network,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getStacksTokenContractInfo = (
|
export const getStacksTokenContractInfo = async (
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
chainId: KnownChainId.StacksChain,
|
chainId: KnownChainId.StacksChain,
|
||||||
tokenId: KnownTokenId.StacksToken,
|
tokenId: KnownTokenId.StacksToken,
|
||||||
): undefined | (StacksContractAddress & { network: StacksNetwork }) => {
|
): Promise<
|
||||||
if (stxTokenContractAddresses[tokenId]?.[chainId] == null) {
|
undefined | (StacksContractAddress & { network: StacksNetwork })
|
||||||
|
> => {
|
||||||
|
let address: StacksContractAddress | undefined
|
||||||
|
if (stxTokenContractAddresses[tokenId]?.[chainId] != null) {
|
||||||
|
address = stxTokenContractAddresses[tokenId][chainId]
|
||||||
|
} else {
|
||||||
|
const allTokens = await getAllStacksTokens(ctx, chainId)
|
||||||
|
for (const token of allTokens) {
|
||||||
|
if (token.stacksTokenId === tokenId) {
|
||||||
|
address = token.contractAddress
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address == null) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const network =
|
const network =
|
||||||
chainId === KnownChainId.Stacks.Mainnet ? STACKS_MAINNET : STACKS_TESTNET
|
chainId === KnownChainId.Stacks.Mainnet ? STACKS_MAINNET : STACKS_TESTNET
|
||||||
|
|
||||||
return {
|
return { ...address, network }
|
||||||
...stxTokenContractAddresses[tokenId]![chainId],
|
|
||||||
network,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStacksToken(
|
export async function getStacksToken(
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
chain: KnownChainId.StacksChain,
|
chain: KnownChainId.StacksChain,
|
||||||
tokenAddress: StacksContractAddress,
|
tokenAddress: StacksContractAddress,
|
||||||
): Promise<undefined | KnownTokenId.StacksToken> {
|
): Promise<undefined | KnownTokenId.StacksToken> {
|
||||||
@@ -109,90 +145,87 @@ export async function getStacksToken(
|
|||||||
const info = stxTokenContractAddresses[token]?.[chain]
|
const info = stxTokenContractAddresses[token]?.[chain]
|
||||||
if (info == null) continue
|
if (info == null) continue
|
||||||
|
|
||||||
if (
|
if (isStacksContractAddressEqual(info, tokenAddress)) {
|
||||||
info.deployerAddress === tokenAddress.deployerAddress &&
|
return token as KnownTokenId.StacksToken
|
||||||
info.contractName === tokenAddress.contractName
|
}
|
||||||
) {
|
}
|
||||||
return token
|
|
||||||
|
const allTokens = await getAllStacksTokens(ctx, chain)
|
||||||
|
for (const token of allTokens) {
|
||||||
|
if (isStacksContractAddressEqual(token.contractAddress, tokenAddress)) {
|
||||||
|
return token.stacksTokenId
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.underlyingToken != null) {
|
||||||
|
if (
|
||||||
|
isStacksContractAddressEqual(
|
||||||
|
token.underlyingToken.contractAddress,
|
||||||
|
tokenAddress,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return token.stacksTokenId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addressFromBuffer(
|
const getAllStacksTokens = (
|
||||||
chain: KnownChainId.KnownChain,
|
ctx: SDKGlobalContext,
|
||||||
buffer: Uint8Array,
|
chain: KnownChainId.StacksChain,
|
||||||
): string {
|
): Promise<StacksTokenInfo[]> => {
|
||||||
if (KnownChainId.isStacksChain(chain)) {
|
const cache = ctx.stacks.tokensCache
|
||||||
return c32address(
|
|
||||||
c32addressVersions[
|
if (cache == null) {
|
||||||
chain === KnownChainId.Stacks.Mainnet ? "mainnet" : "testnet"
|
return getAllStacksTokensImpl(ctx, chain)
|
||||||
].p2pkh,
|
|
||||||
encodeHex(buffer),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const cached = cache.get(chain)
|
||||||
KnownChainId.isBitcoinChain(chain) ||
|
if (cached == null) {
|
||||||
KnownChainId.isBRC20Chain(chain) ||
|
const promise = getAllStacksTokensImpl(ctx, chain).catch(e => {
|
||||||
KnownChainId.isRunesChain(chain)
|
if (cache.get(chain) === promise) {
|
||||||
) {
|
cache.delete(chain)
|
||||||
return scriptPubKeyToAddress(
|
}
|
||||||
chain === KnownChainId.Bitcoin.Mainnet ? NETWORK : TEST_NETWORK,
|
throw e
|
||||||
buffer,
|
})
|
||||||
)
|
cache.set(chain, promise)
|
||||||
}
|
}
|
||||||
|
return cache.get(chain)!
|
||||||
if (KnownChainId.isEVMChain(chain)) {
|
|
||||||
return encodeZeroPrefixedHex(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
checkNever(chain)
|
|
||||||
throw new TypeError("[addressFromBuffer] Unsupported chain: " + chain)
|
|
||||||
}
|
}
|
||||||
|
const getAllStacksTokensImpl = async (
|
||||||
export function addressToBuffer(
|
ctx: SDKGlobalContext,
|
||||||
chain: KnownChainId.KnownChain,
|
chain: KnownChainId.StacksChain,
|
||||||
address: string,
|
): Promise<StacksTokenInfo[]> => {
|
||||||
): Uint8Array {
|
const res = await requestAPI<{ tokens: StacksTokenFromAPI[] }>(ctx, {
|
||||||
if (KnownChainId.isStacksChain(chain)) {
|
method: "GET",
|
||||||
const [version, hash160] = c32addressDecode(address)
|
path: "/2024-10-01/stacks/tokens",
|
||||||
|
query: {
|
||||||
if (
|
network: chain === KnownChainId.Stacks.Mainnet ? "mainnet" : "testnet",
|
||||||
(chain === KnownChainId.Stacks.Mainnet &&
|
},
|
||||||
version == c32addressVersions.mainnet.p2sh) ||
|
})
|
||||||
(chain === KnownChainId.Stacks.Testnet &&
|
return res.tokens.map(info => ({
|
||||||
version == c32addressVersions.testnet.p2sh)
|
stacksTokenId: createStacksToken(info.id),
|
||||||
) {
|
contractAddress: info.contractAddress,
|
||||||
throw new StacksAddressVersionNotSupportedError(address, "Multisig")
|
decimals: info.decimals,
|
||||||
} else if (
|
underlyingToken: info.underlyingToken,
|
||||||
(chain === KnownChainId.Stacks.Mainnet &&
|
}))
|
||||||
version !== c32addressVersions.mainnet.p2pkh) ||
|
}
|
||||||
(chain === KnownChainId.Stacks.Testnet &&
|
export interface StacksTokenInfo {
|
||||||
version !== c32addressVersions.testnet.p2pkh)
|
stacksTokenId: KnownTokenId.StacksToken
|
||||||
) {
|
contractAddress: StacksContractAddress
|
||||||
throw new StacksAddressVersionNotSupportedError(address, `${version}`)
|
decimals: number
|
||||||
}
|
underlyingToken?: {
|
||||||
|
contractAddress: StacksContractAddress
|
||||||
return decodeHex(hash160)
|
decimals: number
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (
|
interface StacksTokenFromAPI {
|
||||||
KnownChainId.isBitcoinChain(chain) ||
|
id: string
|
||||||
KnownChainId.isBRC20Chain(chain) ||
|
contractAddress: StacksContractAddress
|
||||||
KnownChainId.isRunesChain(chain)
|
decimals: number
|
||||||
) {
|
underlyingToken?: {
|
||||||
return addressToScriptPubKey(
|
contractAddress: StacksContractAddress
|
||||||
chain === KnownChainId.Bitcoin.Mainnet ? NETWORK : TEST_NETWORK,
|
decimals: number
|
||||||
address,
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (KnownChainId.isEVMChain(chain)) {
|
|
||||||
return decodeHex(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
checkNever(chain)
|
|
||||||
throw new TypeError("[addressToBuffer] Unsupported chain: " + chain)
|
|
||||||
}
|
}
|
||||||
|
|||||||
110
src/utils/Optional.ts
Normal file
110
src/utils/Optional.ts
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
export type Optional<T> = Optional.Some<T> | Optional.None
|
||||||
|
|
||||||
|
export namespace Optional {
|
||||||
|
export interface Some<T> {
|
||||||
|
type: "some"
|
||||||
|
payload: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface None {
|
||||||
|
type: "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isOptional = <T>(input: any): input is Optional<T> => {
|
||||||
|
if (input == null) return false
|
||||||
|
if (typeof input !== "object") return false
|
||||||
|
if (input.type !== "some" && input.type !== "none") return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export const some = <const TOK>(payload: TOK): Optional.Some<TOK> => ({
|
||||||
|
type: "some",
|
||||||
|
payload,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const none = (): Optional.None => ({
|
||||||
|
type: "none",
|
||||||
|
})
|
||||||
|
|
||||||
|
export const maybeSome = <T>(res: Optional<T>): undefined | T => {
|
||||||
|
if (res.type === "some") return res.payload
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encaseOptional<TOK>(fn: () => TOK): Optional<TOK> {
|
||||||
|
try {
|
||||||
|
return some(fn())
|
||||||
|
} catch (e: unknown) {
|
||||||
|
return none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encaseMaybeSome<TOK>(fn: () => TOK): undefined | TOK
|
||||||
|
export function encaseMaybeSome<TOK, TFallback>(
|
||||||
|
fallbackValue: TFallback,
|
||||||
|
fn: () => TOK,
|
||||||
|
): TOK | TFallback
|
||||||
|
export function encaseMaybeSome<TOK, TFallback>(
|
||||||
|
...args: [fallbackValue: TFallback, fn: () => TOK] | [fn: () => TOK]
|
||||||
|
): undefined | TOK | TFallback {
|
||||||
|
const [fallbackValue, fn] = args.length === 1 ? [undefined, args[0]] : args
|
||||||
|
const res = encaseOptional(fn)
|
||||||
|
return res.type === "some" ? res.payload : fallbackValue
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CurriedMapFn {
|
||||||
|
<TOKInput, TOKOutput>(
|
||||||
|
mapping: (payload: TOKInput) => TOKOutput,
|
||||||
|
): (res: Optional<TOKInput>) => Optional<TOKOutput>
|
||||||
|
<TOKInput, TOKOutput>(
|
||||||
|
mapping: (payload: TOKInput) => TOKOutput,
|
||||||
|
res: Optional<TOKInput>,
|
||||||
|
): Optional<TOKOutput>
|
||||||
|
}
|
||||||
|
export const map: CurriedMapFn = <TOKInput, TOKOutput>(
|
||||||
|
mapping: (payload: TOKInput) => TOKOutput,
|
||||||
|
res?: Optional<TOKInput>,
|
||||||
|
): any => {
|
||||||
|
if (res == null) {
|
||||||
|
return (res: Optional<TOKInput>) => realMap(mapping, res)
|
||||||
|
} else {
|
||||||
|
return realMap(mapping, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
function realMap(
|
||||||
|
mapping: (payload: TOKInput) => TOKOutput,
|
||||||
|
res: Optional<TOKInput>,
|
||||||
|
): Optional<TOKOutput> {
|
||||||
|
if (res.type === "none") return res
|
||||||
|
return Optional.some(mapping(res.payload))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CurriedFlatMapFn {
|
||||||
|
<TOKInput, TOKOutput>(
|
||||||
|
mapping: (payload: TOKInput) => Optional<TOKOutput>,
|
||||||
|
): (res: Optional<TOKInput>) => Optional<TOKOutput>
|
||||||
|
<TOKInput, TOKOutput>(
|
||||||
|
mapping: (payload: TOKInput) => Optional<TOKOutput>,
|
||||||
|
res: Optional<TOKInput>,
|
||||||
|
): Optional<TOKOutput>
|
||||||
|
}
|
||||||
|
export const flatMap: CurriedFlatMapFn = <TOKInput, TOKOutput>(
|
||||||
|
mapping: (payload: TOKInput) => Optional<TOKOutput>,
|
||||||
|
res?: Optional<TOKInput>,
|
||||||
|
): any => {
|
||||||
|
if (res == null) {
|
||||||
|
return (res: Optional<TOKInput>) => realFlatMap(mapping, res)
|
||||||
|
} else {
|
||||||
|
return realFlatMap(mapping, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
function realFlatMap(
|
||||||
|
mapping: (payload: TOKInput) => Optional<TOKOutput>,
|
||||||
|
res: Optional<TOKInput>,
|
||||||
|
): Optional<TOKOutput> {
|
||||||
|
if (res.type === "none") return res
|
||||||
|
return mapping(res.payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
164
src/utils/Result.ts
Normal file
164
src/utils/Result.ts
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
export type Result<TOK, TError> = Result.OK<TOK> | Result.Error<TError>
|
||||||
|
|
||||||
|
export namespace Result {
|
||||||
|
export interface OK<T> {
|
||||||
|
type: "ok"
|
||||||
|
payload: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Error<T> {
|
||||||
|
type: "error"
|
||||||
|
payload: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isResult = <TOK, TErr>(
|
||||||
|
input: any,
|
||||||
|
): input is Result<TOK, TErr> => {
|
||||||
|
if (input == null) return false
|
||||||
|
if (typeof input !== "object") return false
|
||||||
|
if (input.type !== "ok" && input.type !== "error") return false
|
||||||
|
if (!("payload" in input)) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ok = <const TOK>(payload: TOK): Result.OK<TOK> => ({
|
||||||
|
type: "ok",
|
||||||
|
payload,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const error = <const TError>(
|
||||||
|
payload: TError,
|
||||||
|
): Result.Error<TError> => ({
|
||||||
|
type: "error",
|
||||||
|
payload,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const maybeValue = <TOK, TError>(
|
||||||
|
res: Result<TOK, TError>,
|
||||||
|
): undefined | TOK => {
|
||||||
|
if (res.type === "ok") return res.payload
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
export const maybeError = <TOK, TError>(
|
||||||
|
res: Result<TOK, TError>,
|
||||||
|
): undefined | TError => {
|
||||||
|
if (res.type === "error") return res.payload
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
export const encase = <TOK, TError = unknown>(
|
||||||
|
fn: () => TOK,
|
||||||
|
): Result<TOK, TError> => {
|
||||||
|
try {
|
||||||
|
return ok(fn())
|
||||||
|
} catch (e: unknown) {
|
||||||
|
return error(e) as any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function encaseMaybeValue<TOK>(fn: () => TOK): undefined | TOK
|
||||||
|
export function encaseMaybeValue<TOK, TFallback>(
|
||||||
|
fallbackValue: TFallback,
|
||||||
|
fn: () => TOK,
|
||||||
|
): TOK | TFallback
|
||||||
|
export function encaseMaybeValue<TOK, TFallback>(
|
||||||
|
...args: [fallbackValue: TFallback, fn: () => TOK] | [fn: () => TOK]
|
||||||
|
): undefined | TOK | TFallback {
|
||||||
|
const [fallbackValue, fn] = args.length === 1 ? [undefined, args[0]] : args
|
||||||
|
const res = encase(fn)
|
||||||
|
return res.type === "ok" ? res.payload : fallbackValue
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CurriedMapFn {
|
||||||
|
<TOKInput, TOKOutput, TError>(
|
||||||
|
mapping: (payload: TOKInput) => TOKOutput,
|
||||||
|
): (res: Result<TOKInput, TError>) => Result<TOKOutput, TError>
|
||||||
|
<TOKInput, TOKOutput, TError>(
|
||||||
|
mapping: (payload: TOKInput) => TOKOutput,
|
||||||
|
res: Result<TOKInput, TError>,
|
||||||
|
): Result<TOKOutput, TError>
|
||||||
|
}
|
||||||
|
export const map: CurriedMapFn = <TOKInput, TOKOutput, TError>(
|
||||||
|
mapping: (payload: TOKInput) => TOKOutput,
|
||||||
|
res?: Result<TOKInput, TError>,
|
||||||
|
): any => {
|
||||||
|
if (res == null) {
|
||||||
|
return (res: Result<TOKInput, TError>) => realMap(mapping, res)
|
||||||
|
} else {
|
||||||
|
return realMap(mapping, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
function realMap(
|
||||||
|
mapping: (payload: TOKInput) => TOKOutput,
|
||||||
|
res: Result<TOKInput, TError>,
|
||||||
|
): Result<TOKOutput, TError> {
|
||||||
|
if (res.type === "error") return res
|
||||||
|
return Result.ok(mapping(res.payload))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CurriedChainErrorFn {
|
||||||
|
<TOKInput, TErrorInput, TOKOutput, TErrorOutput>(
|
||||||
|
mapping: (payload: TErrorInput) => Result<TOKOutput, TErrorOutput>,
|
||||||
|
): (
|
||||||
|
res: Result<TOKInput, TErrorInput>,
|
||||||
|
) => Result<TOKInput | TOKOutput, TErrorOutput>
|
||||||
|
<TOKInput, TErrorInput, TOKOutput, TErrorOutput>(
|
||||||
|
mapping: (payload: TErrorInput) => Result<TOKOutput, TErrorOutput>,
|
||||||
|
res: Result<TOKInput, TErrorInput>,
|
||||||
|
): Result<TOKInput | TOKOutput, TErrorOutput>
|
||||||
|
}
|
||||||
|
export const chainError: CurriedChainErrorFn = <
|
||||||
|
TOKInput,
|
||||||
|
TErrorInput,
|
||||||
|
TOKOutput,
|
||||||
|
TErrorOutput,
|
||||||
|
>(
|
||||||
|
chainFn: (payload: TErrorInput) => Result<TOKOutput, TErrorOutput>,
|
||||||
|
res?: Result<TOKInput, TErrorInput>,
|
||||||
|
): any => {
|
||||||
|
if (res == null) {
|
||||||
|
return (res: Result<TOKInput, TErrorInput>) =>
|
||||||
|
realChainError(chainFn, res)
|
||||||
|
} else {
|
||||||
|
return realChainError(chainFn, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
function realChainError(
|
||||||
|
chainFn: (payload: TErrorInput) => Result<TOKOutput, TErrorOutput>,
|
||||||
|
res: Result<TOKInput, TErrorInput>,
|
||||||
|
): Result<TOKInput | TOKOutput, TErrorOutput> {
|
||||||
|
if (res.type !== "error") return res
|
||||||
|
return chainFn(res.payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CurriedFlatMapFn {
|
||||||
|
<TOKInput, TOKOutput, TError>(
|
||||||
|
mapping: (payload: TOKInput) => Result<TOKOutput, TError>,
|
||||||
|
): (res: Result<TOKInput, TError>) => Result<TOKOutput, TError>
|
||||||
|
<TOKInput, TOKOutput, TError>(
|
||||||
|
mapping: (payload: TOKInput) => Result<TOKOutput, TError>,
|
||||||
|
res: Result<TOKInput, TError>,
|
||||||
|
): Result<TOKOutput, TError>
|
||||||
|
}
|
||||||
|
export const flatMap: CurriedFlatMapFn = <TOKInput, TOKOutput, TError>(
|
||||||
|
mapping: (payload: TOKInput) => Result<TOKOutput, TError>,
|
||||||
|
res?: Result<TOKInput, TError>,
|
||||||
|
): any => {
|
||||||
|
if (res == null) {
|
||||||
|
return (res: Result<TOKInput, TError>) => realMap(mapping, res)
|
||||||
|
} else {
|
||||||
|
return realMap(mapping, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
function realMap(
|
||||||
|
mapping: (payload: TOKInput) => Result<TOKOutput, TError>,
|
||||||
|
res: Result<TOKInput, TError>,
|
||||||
|
): Result<TOKOutput, TError> {
|
||||||
|
if (res.type === "error") return res
|
||||||
|
return mapping(res.payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const chainOk = flatMap
|
||||||
|
}
|
||||||
16
src/utils/ResultHelpers.ts
Normal file
16
src/utils/ResultHelpers.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { identity } from "./funcHelpers"
|
||||||
|
import { Result } from "./Result"
|
||||||
|
|
||||||
|
export function encase<T, T1>(
|
||||||
|
mapping: (res: T) => T1,
|
||||||
|
fn: () => T,
|
||||||
|
): undefined | T1
|
||||||
|
export function encase<T>(fn: () => T): undefined | T
|
||||||
|
export function encase<T, T1>(
|
||||||
|
...args: [mapping: (res: T) => T1, fn: () => T] | [fn: () => T]
|
||||||
|
): undefined | T1 {
|
||||||
|
const [mapping, fn] = args.length === 1 ? [undefined, args[0]] : args
|
||||||
|
return Result.maybeValue(
|
||||||
|
Result.map(mapping ?? (identity as any), Result.encase(fn)),
|
||||||
|
)
|
||||||
|
}
|
||||||
164
src/utils/SwapRouteHelpers.ts
Normal file
164
src/utils/SwapRouteHelpers.ts
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
import { evmTokenToCorrespondingStacksToken } from "../evmUtils/peggingHelpers"
|
||||||
|
import { metaTokenToCorrespondingStacksToken } from "../metaUtils/peggingHelpers"
|
||||||
|
import { getStacksToken } from "../stacksUtils/xlinkContractHelpers"
|
||||||
|
import { SDKNumber, StacksContractAddress } from "../xlinkSdkUtils/types"
|
||||||
|
import { SDKGlobalContext } from "../xlinkSdkUtils/types.internal"
|
||||||
|
import { last } from "./arrayHelpers"
|
||||||
|
import { BigNumber } from "./BigNumber"
|
||||||
|
import { KnownRoute_WithMetaProtocol } from "./buildSupportedRoutes"
|
||||||
|
import { UnsupportedBridgeRouteError } from "./errors"
|
||||||
|
import { checkNever, OneOrMore } from "./typeHelpers"
|
||||||
|
import { KnownChainId, KnownTokenId } from "./types/knownIds"
|
||||||
|
|
||||||
|
export interface SwapRoute {
|
||||||
|
fromTokenAddress: StacksContractAddress
|
||||||
|
swapPools: OneOrMore<{
|
||||||
|
poolId: bigint
|
||||||
|
toTokenAddress: StacksContractAddress
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SwapRoute_WithExchangeRate extends SwapRoute {
|
||||||
|
composedExchangeRate: BigNumber
|
||||||
|
}
|
||||||
|
export interface SwapRoute_WithExchangeRate_Public extends SwapRoute {
|
||||||
|
composedExchangeRate: SDKNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SwapRoute_WithMinimumAmountsToReceive extends SwapRoute {
|
||||||
|
minimumAmountsToReceive: BigNumber
|
||||||
|
}
|
||||||
|
export interface SwapRoute_WithMinimumAmountsToReceive_Public
|
||||||
|
extends SwapRoute {
|
||||||
|
minimumAmountsToReceive: SDKNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getFirstStepStacksTokenAddress(
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
|
info: {
|
||||||
|
swap: SwapRoute
|
||||||
|
stacksChain: KnownChainId.StacksChain
|
||||||
|
},
|
||||||
|
): Promise<undefined | KnownTokenId.StacksToken> {
|
||||||
|
return getStacksToken(
|
||||||
|
sdkContext,
|
||||||
|
info.stacksChain,
|
||||||
|
info.swap.fromTokenAddress,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getFinalStepStacksTokenAddress(
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
|
info: {
|
||||||
|
swap: SwapRoute
|
||||||
|
stacksChain: KnownChainId.StacksChain
|
||||||
|
},
|
||||||
|
): Promise<undefined | KnownTokenId.StacksToken> {
|
||||||
|
const finalStepStacksTokenAddress = last(info.swap.swapPools).toTokenAddress
|
||||||
|
|
||||||
|
return getStacksToken(
|
||||||
|
sdkContext,
|
||||||
|
info.stacksChain,
|
||||||
|
finalStepStacksTokenAddress,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTransitStacksChainTransitStepInfos(
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
|
info: KnownRoute_WithMetaProtocol & {
|
||||||
|
swapRoute?: SwapRoute_WithExchangeRate_Public
|
||||||
|
},
|
||||||
|
): Promise<{
|
||||||
|
step1ToStacksToken: KnownTokenId.StacksToken
|
||||||
|
step2FromStacksToken: KnownTokenId.StacksToken
|
||||||
|
}> {
|
||||||
|
const transitStacksChainId =
|
||||||
|
info.fromChain === KnownChainId.Bitcoin.Mainnet ||
|
||||||
|
info.fromChain === KnownChainId.BRC20.Mainnet ||
|
||||||
|
info.fromChain === KnownChainId.Runes.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet
|
||||||
|
|
||||||
|
const [
|
||||||
|
step1ToStacksToken,
|
||||||
|
step2FromStacksToken,
|
||||||
|
swapStartToken,
|
||||||
|
swapEndToken,
|
||||||
|
] = await Promise.all([
|
||||||
|
toCorrespondingStacksToken(info.fromChain, info.fromToken),
|
||||||
|
toCorrespondingStacksToken(info.toChain, info.toToken),
|
||||||
|
info.swapRoute == null
|
||||||
|
? null
|
||||||
|
: getFirstStepStacksTokenAddress(ctx, {
|
||||||
|
swap: info.swapRoute,
|
||||||
|
stacksChain: transitStacksChainId,
|
||||||
|
}),
|
||||||
|
info.swapRoute == null
|
||||||
|
? null
|
||||||
|
: getFinalStepStacksTokenAddress(ctx, {
|
||||||
|
swap: info.swapRoute,
|
||||||
|
stacksChain: transitStacksChainId,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
if (
|
||||||
|
step1ToStacksToken == null ||
|
||||||
|
step2FromStacksToken == null ||
|
||||||
|
(info.swapRoute != null &&
|
||||||
|
(swapStartToken !== step1ToStacksToken ||
|
||||||
|
swapEndToken !== step2FromStacksToken))
|
||||||
|
) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
info.fromToken,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
step1ToStacksToken,
|
||||||
|
step2FromStacksToken,
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toCorrespondingStacksToken(
|
||||||
|
chain: KnownChainId.KnownChain,
|
||||||
|
token: KnownTokenId.KnownToken,
|
||||||
|
): Promise<undefined | KnownTokenId.StacksToken> {
|
||||||
|
let toStacksTokenPromise:
|
||||||
|
| undefined
|
||||||
|
| Promise<undefined | KnownTokenId.StacksToken>
|
||||||
|
|
||||||
|
if (KnownChainId.isBitcoinChain(chain)) {
|
||||||
|
if (token === KnownTokenId.Bitcoin.BTC) {
|
||||||
|
toStacksTokenPromise = Promise.resolve(KnownTokenId.Stacks.aBTC)
|
||||||
|
}
|
||||||
|
} else if (KnownChainId.isBRC20Chain(chain)) {
|
||||||
|
if (KnownTokenId.isBRC20Token(token)) {
|
||||||
|
toStacksTokenPromise = metaTokenToCorrespondingStacksToken(ctx, {
|
||||||
|
chain: chain as KnownChainId.BRC20Chain,
|
||||||
|
token: token as KnownTokenId.BRC20Token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (KnownChainId.isRunesChain(chain)) {
|
||||||
|
if (KnownTokenId.isRunesToken(token)) {
|
||||||
|
toStacksTokenPromise = metaTokenToCorrespondingStacksToken(ctx, {
|
||||||
|
chain: chain as KnownChainId.RunesChain,
|
||||||
|
token: token as KnownTokenId.RunesToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (KnownChainId.isEVMChain(chain)) {
|
||||||
|
if (KnownTokenId.isEVMToken(token)) {
|
||||||
|
toStacksTokenPromise = evmTokenToCorrespondingStacksToken(token)
|
||||||
|
}
|
||||||
|
} else if (KnownChainId.isStacksChain(chain)) {
|
||||||
|
if (KnownTokenId.isStacksToken(token)) {
|
||||||
|
toStacksTokenPromise = Promise.resolve(token)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
checkNever(chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
return toStacksTokenPromise
|
||||||
|
}
|
||||||
|
}
|
||||||
105
src/utils/addressHelpers.ts
Normal file
105
src/utils/addressHelpers.ts
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import { NETWORK, TEST_NETWORK } from "@scure/btc-signer"
|
||||||
|
import {
|
||||||
|
c32address,
|
||||||
|
c32addressDecode,
|
||||||
|
versions as c32addressVersions,
|
||||||
|
} from "c32check"
|
||||||
|
import {
|
||||||
|
addressToScriptPubKey,
|
||||||
|
scriptPubKeyToAddress,
|
||||||
|
} from "../bitcoinUtils/bitcoinHelpers"
|
||||||
|
import { StacksAddressVersionNotSupportedError } from "./errors"
|
||||||
|
import { decodeHex, encodeHex, encodeZeroPrefixedHex } from "./hexHelpers"
|
||||||
|
import { checkNever } from "./typeHelpers"
|
||||||
|
import { KnownChainId } from "./types/knownIds"
|
||||||
|
|
||||||
|
export function addressFromBuffer(
|
||||||
|
chain: KnownChainId.KnownChain,
|
||||||
|
buffer: Uint8Array,
|
||||||
|
): string {
|
||||||
|
if (KnownChainId.isStacksChain(chain)) {
|
||||||
|
return c32address(
|
||||||
|
c32addressVersions[
|
||||||
|
chain === KnownChainId.Stacks.Mainnet ? "mainnet" : "testnet"
|
||||||
|
].p2pkh,
|
||||||
|
encodeHex(buffer),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
KnownChainId.isBitcoinChain(chain) ||
|
||||||
|
KnownChainId.isBRC20Chain(chain) ||
|
||||||
|
KnownChainId.isRunesChain(chain)
|
||||||
|
) {
|
||||||
|
const network =
|
||||||
|
chain === KnownChainId.Bitcoin.Mainnet ||
|
||||||
|
chain === KnownChainId.BRC20.Mainnet ||
|
||||||
|
chain === KnownChainId.Runes.Mainnet
|
||||||
|
? NETWORK
|
||||||
|
: chain === KnownChainId.Bitcoin.Testnet ||
|
||||||
|
chain === KnownChainId.BRC20.Testnet ||
|
||||||
|
chain === KnownChainId.Runes.Testnet
|
||||||
|
? TEST_NETWORK
|
||||||
|
: (checkNever(chain), NETWORK)
|
||||||
|
return scriptPubKeyToAddress(network, buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (KnownChainId.isEVMChain(chain)) {
|
||||||
|
return encodeZeroPrefixedHex(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNever(chain)
|
||||||
|
throw new TypeError("[addressFromBuffer] Unsupported chain: " + chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addressToBuffer(
|
||||||
|
chain: KnownChainId.KnownChain,
|
||||||
|
address: string,
|
||||||
|
): Uint8Array {
|
||||||
|
if (KnownChainId.isStacksChain(chain)) {
|
||||||
|
const [version, hash160] = c32addressDecode(address)
|
||||||
|
|
||||||
|
if (
|
||||||
|
(chain === KnownChainId.Stacks.Mainnet &&
|
||||||
|
version == c32addressVersions.mainnet.p2sh) ||
|
||||||
|
(chain === KnownChainId.Stacks.Testnet &&
|
||||||
|
version == c32addressVersions.testnet.p2sh)
|
||||||
|
) {
|
||||||
|
throw new StacksAddressVersionNotSupportedError(address, "Multisig")
|
||||||
|
} else if (
|
||||||
|
(chain === KnownChainId.Stacks.Mainnet &&
|
||||||
|
version !== c32addressVersions.mainnet.p2pkh) ||
|
||||||
|
(chain === KnownChainId.Stacks.Testnet &&
|
||||||
|
version !== c32addressVersions.testnet.p2pkh)
|
||||||
|
) {
|
||||||
|
throw new StacksAddressVersionNotSupportedError(address, `${version}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodeHex(hash160)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
KnownChainId.isBitcoinChain(chain) ||
|
||||||
|
KnownChainId.isBRC20Chain(chain) ||
|
||||||
|
KnownChainId.isRunesChain(chain)
|
||||||
|
) {
|
||||||
|
const network =
|
||||||
|
chain === KnownChainId.Bitcoin.Mainnet ||
|
||||||
|
chain === KnownChainId.BRC20.Mainnet ||
|
||||||
|
chain === KnownChainId.Runes.Mainnet
|
||||||
|
? NETWORK
|
||||||
|
: chain === KnownChainId.Bitcoin.Testnet ||
|
||||||
|
chain === KnownChainId.BRC20.Testnet ||
|
||||||
|
chain === KnownChainId.Runes.Testnet
|
||||||
|
? TEST_NETWORK
|
||||||
|
: (checkNever(chain), NETWORK)
|
||||||
|
return addressToScriptPubKey(network, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (KnownChainId.isEVMChain(chain)) {
|
||||||
|
return decodeHex(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNever(chain)
|
||||||
|
throw new TypeError("[addressToBuffer] Unsupported chain: " + chain)
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { ChainId, TokenId } from "../xlinkSdkUtils/types"
|
import { ChainId, TokenId } from "../xlinkSdkUtils/types"
|
||||||
|
import { SwapRoute } from "./SwapRouteHelpers"
|
||||||
import { SDKGlobalContext } from "../xlinkSdkUtils/types.internal"
|
import { SDKGlobalContext } from "../xlinkSdkUtils/types.internal"
|
||||||
import { UnsupportedBridgeRouteError } from "./errors"
|
import { UnsupportedBridgeRouteError } from "./errors"
|
||||||
import { pMemoize } from "./pMemoize"
|
import { pMemoize } from "./pMemoize"
|
||||||
@@ -53,23 +54,23 @@ export type KnownRoute_FromBitcoin_ToEVM = {
|
|||||||
toChain: KnownChainId.EVMChain
|
toChain: KnownChainId.EVMChain
|
||||||
toToken: KnownTokenId.EVMToken
|
toToken: KnownTokenId.EVMToken
|
||||||
}
|
}
|
||||||
// export type KnownRoute_FromBitcoin_ToBRC20 = {
|
export type KnownRoute_FromBitcoin_ToBRC20 = {
|
||||||
// fromChain: KnownChainId.BitcoinChain
|
fromChain: KnownChainId.BitcoinChain
|
||||||
// fromToken: KnownTokenId.BitcoinToken
|
fromToken: KnownTokenId.BitcoinToken
|
||||||
// toChain: KnownChainId.BRC20Chain
|
toChain: KnownChainId.BRC20Chain
|
||||||
// toToken: KnownTokenId.BRC20Token
|
toToken: KnownTokenId.BRC20Token
|
||||||
// }
|
}
|
||||||
// export type KnownRoute_FromBitcoin_ToRunes = {
|
export type KnownRoute_FromBitcoin_ToRunes = {
|
||||||
// fromChain: KnownChainId.BitcoinChain
|
fromChain: KnownChainId.BitcoinChain
|
||||||
// fromToken: KnownTokenId.BitcoinToken
|
fromToken: KnownTokenId.BitcoinToken
|
||||||
// toChain: KnownChainId.RunesChain
|
toChain: KnownChainId.RunesChain
|
||||||
// toToken: KnownTokenId.RunesToken
|
toToken: KnownTokenId.RunesToken
|
||||||
// }
|
}
|
||||||
export type KnownRoute_FromBitcoin =
|
export type KnownRoute_FromBitcoin =
|
||||||
| KnownRoute_FromBitcoin_ToStacks
|
| KnownRoute_FromBitcoin_ToStacks
|
||||||
| KnownRoute_FromBitcoin_ToEVM
|
| KnownRoute_FromBitcoin_ToEVM
|
||||||
// | KnownRoute_FromBitcoin_ToBRC20
|
| KnownRoute_FromBitcoin_ToBRC20
|
||||||
// | KnownRoute_FromBitcoin_ToRunes
|
| KnownRoute_FromBitcoin_ToRunes
|
||||||
|
|
||||||
export type KnownRoute_FromEVM_ToStacks = {
|
export type KnownRoute_FromEVM_ToStacks = {
|
||||||
fromChain: KnownChainId.EVMChain
|
fromChain: KnownChainId.EVMChain
|
||||||
@@ -108,24 +109,98 @@ export type KnownRoute_FromEVM =
|
|||||||
| KnownRoute_FromEVM_ToRunes
|
| KnownRoute_FromEVM_ToRunes
|
||||||
| KnownRoute_FromEVM_ToEVM
|
| KnownRoute_FromEVM_ToEVM
|
||||||
|
|
||||||
export type _KnownRoute_FromBRC20_ToStacks = {
|
export type KnownRoute_FromBRC20_ToStacks = {
|
||||||
fromChain: KnownChainId.BRC20Chain
|
fromChain: KnownChainId.BRC20Chain
|
||||||
fromToken: KnownTokenId.BRC20Token
|
fromToken: KnownTokenId.BRC20Token
|
||||||
toChain: KnownChainId.StacksChain
|
toChain: KnownChainId.StacksChain
|
||||||
toToken: KnownTokenId.StacksToken
|
toToken: KnownTokenId.StacksToken
|
||||||
}
|
}
|
||||||
export type _KnownRoute_FromRunes_ToStacks = {
|
export type KnownRoute_FromBRC20_ToEVM = {
|
||||||
|
fromChain: KnownChainId.BRC20Chain
|
||||||
|
fromToken: KnownTokenId.BRC20Token
|
||||||
|
toChain: KnownChainId.EVMChain
|
||||||
|
toToken: KnownTokenId.EVMToken
|
||||||
|
}
|
||||||
|
export type KnownRoute_FromBRC20_ToBitcoin = {
|
||||||
|
fromChain: KnownChainId.BRC20Chain
|
||||||
|
fromToken: KnownTokenId.BRC20Token
|
||||||
|
toChain: KnownChainId.BitcoinChain
|
||||||
|
toToken: KnownTokenId.BitcoinToken
|
||||||
|
}
|
||||||
|
export type KnownRoute_FromBRC20_ToBRC20 = {
|
||||||
|
fromChain: KnownChainId.BRC20Chain
|
||||||
|
fromToken: KnownTokenId.BRC20Token
|
||||||
|
toChain: KnownChainId.BRC20Chain
|
||||||
|
toToken: KnownTokenId.BRC20Token
|
||||||
|
}
|
||||||
|
export type KnownRoute_FromBRC20_ToRunes = {
|
||||||
|
fromChain: KnownChainId.BRC20Chain
|
||||||
|
fromToken: KnownTokenId.BRC20Token
|
||||||
|
toChain: KnownChainId.RunesChain
|
||||||
|
toToken: KnownTokenId.RunesToken
|
||||||
|
}
|
||||||
|
export type _KnownRoute_FromBRC20 =
|
||||||
|
| KnownRoute_FromBRC20_ToStacks
|
||||||
|
| KnownRoute_FromBRC20_ToEVM
|
||||||
|
| KnownRoute_FromBRC20_ToBitcoin
|
||||||
|
| KnownRoute_FromBRC20_ToBRC20
|
||||||
|
| KnownRoute_FromBRC20_ToRunes
|
||||||
|
|
||||||
|
export type KnownRoute_FromRunes_ToStacks = {
|
||||||
fromChain: KnownChainId.RunesChain
|
fromChain: KnownChainId.RunesChain
|
||||||
fromToken: KnownTokenId.RunesToken
|
fromToken: KnownTokenId.RunesToken
|
||||||
toChain: KnownChainId.StacksChain
|
toChain: KnownChainId.StacksChain
|
||||||
toToken: KnownTokenId.StacksToken
|
toToken: KnownTokenId.StacksToken
|
||||||
}
|
}
|
||||||
|
export type KnownRoute_FromRunes_ToEVM = {
|
||||||
|
fromChain: KnownChainId.RunesChain
|
||||||
|
fromToken: KnownTokenId.RunesToken
|
||||||
|
toChain: KnownChainId.EVMChain
|
||||||
|
toToken: KnownTokenId.EVMToken
|
||||||
|
}
|
||||||
|
export type KnownRoute_FromRunes_ToBitcoin = {
|
||||||
|
fromChain: KnownChainId.RunesChain
|
||||||
|
fromToken: KnownTokenId.RunesToken
|
||||||
|
toChain: KnownChainId.BitcoinChain
|
||||||
|
toToken: KnownTokenId.BitcoinToken
|
||||||
|
}
|
||||||
|
export type KnownRoute_FromRunes_ToBRC20 = {
|
||||||
|
fromChain: KnownChainId.RunesChain
|
||||||
|
fromToken: KnownTokenId.RunesToken
|
||||||
|
toChain: KnownChainId.BRC20Chain
|
||||||
|
toToken: KnownTokenId.BRC20Token
|
||||||
|
}
|
||||||
|
export type KnownRoute_FromRunes_ToRunes = {
|
||||||
|
fromChain: KnownChainId.RunesChain
|
||||||
|
fromToken: KnownTokenId.RunesToken
|
||||||
|
toChain: KnownChainId.RunesChain
|
||||||
|
toToken: KnownTokenId.RunesToken
|
||||||
|
}
|
||||||
|
export type _KnownRoute_FromRunes =
|
||||||
|
| KnownRoute_FromRunes_ToStacks
|
||||||
|
| KnownRoute_FromRunes_ToEVM
|
||||||
|
| KnownRoute_FromRunes_ToBitcoin
|
||||||
|
| KnownRoute_FromRunes_ToBRC20
|
||||||
|
| KnownRoute_FromRunes_ToRunes
|
||||||
|
|
||||||
export type KnownRoute =
|
export type KnownRoute =
|
||||||
| KnownRoute_FromStacks
|
| KnownRoute_FromStacks
|
||||||
| KnownRoute_FromBitcoin
|
| KnownRoute_FromBitcoin
|
||||||
| KnownRoute_FromEVM
|
| KnownRoute_FromEVM
|
||||||
|
|
||||||
|
export type KnownRoute_WithMetaProtocol =
|
||||||
|
| KnownRoute_FromStacks
|
||||||
|
| KnownRoute_FromEVM
|
||||||
|
| KnownRoute_FromBitcoin
|
||||||
|
| _KnownRoute_FromBRC20
|
||||||
|
| _KnownRoute_FromRunes
|
||||||
|
|
||||||
|
export type KnownRoute_ToStacks =
|
||||||
|
| KnownRoute_FromBitcoin_ToStacks
|
||||||
|
| KnownRoute_FromEVM_ToStacks
|
||||||
|
| KnownRoute_FromBRC20_ToStacks
|
||||||
|
| KnownRoute_FromRunes_ToStacks
|
||||||
|
|
||||||
export function defineRoute(
|
export function defineRoute(
|
||||||
chainPairs: [fromChains: ChainId[], toChains: ChainId[]],
|
chainPairs: [fromChains: ChainId[], toChains: ChainId[]],
|
||||||
tokenPairs: [fromToken: TokenId, toToken: TokenId][],
|
tokenPairs: [fromToken: TokenId, toToken: TokenId][],
|
||||||
@@ -150,7 +225,9 @@ export function defineRoute(
|
|||||||
|
|
||||||
export type IsSupportedFn = (
|
export type IsSupportedFn = (
|
||||||
ctx: SDKGlobalContext,
|
ctx: SDKGlobalContext,
|
||||||
route: DefinedRoute,
|
route: DefinedRoute & {
|
||||||
|
swapRoute?: SwapRoute
|
||||||
|
},
|
||||||
) => Promise<boolean>
|
) => Promise<boolean>
|
||||||
const memoizedIsSupportedFactory = (
|
const memoizedIsSupportedFactory = (
|
||||||
isSupported: IsSupportedFn,
|
isSupported: IsSupportedFn,
|
||||||
@@ -158,7 +235,12 @@ const memoizedIsSupportedFactory = (
|
|||||||
return pMemoize(
|
return pMemoize(
|
||||||
{
|
{
|
||||||
cacheKey([, route]) {
|
cacheKey([, route]) {
|
||||||
return `${route.fromChain}:${route.fromToken}->${route.toChain}:${route.toToken}`
|
const from = `${route.fromChain}:${route.fromToken}`
|
||||||
|
const to = `${route.toChain}:${route.toToken}`
|
||||||
|
if (route.swapRoute == null) return `${from}->${to}`
|
||||||
|
|
||||||
|
const swap = route.swapRoute.swapPools.map(p => p.poolId).join("->")
|
||||||
|
return `${from}->(${swap})->${to}`
|
||||||
},
|
},
|
||||||
skipCache: true,
|
skipCache: true,
|
||||||
},
|
},
|
||||||
@@ -168,8 +250,8 @@ const memoizedIsSupportedFactory = (
|
|||||||
|
|
||||||
export interface GetSupportedRoutesFn_Conditions {
|
export interface GetSupportedRoutesFn_Conditions {
|
||||||
fromChain?: ChainId
|
fromChain?: ChainId
|
||||||
toChain?: ChainId
|
|
||||||
fromToken?: TokenId
|
fromToken?: TokenId
|
||||||
|
toChain?: ChainId
|
||||||
toToken?: TokenId
|
toToken?: TokenId
|
||||||
}
|
}
|
||||||
export type GetSupportedRoutesFn = (
|
export type GetSupportedRoutesFn = (
|
||||||
@@ -179,7 +261,9 @@ export type GetSupportedRoutesFn = (
|
|||||||
|
|
||||||
export type CheckRouteValidFn = (
|
export type CheckRouteValidFn = (
|
||||||
ctx: SDKGlobalContext,
|
ctx: SDKGlobalContext,
|
||||||
route: DefinedRoute,
|
route: DefinedRoute & {
|
||||||
|
swapRoute?: SwapRoute
|
||||||
|
},
|
||||||
) => Promise<KnownRoute>
|
) => Promise<KnownRoute>
|
||||||
|
|
||||||
export function buildSupportedRoutes(
|
export function buildSupportedRoutes(
|
||||||
|
|||||||
@@ -19,12 +19,34 @@ export interface TransferProphetAppliedResult {
|
|||||||
export const applyTransferProphets = (
|
export const applyTransferProphets = (
|
||||||
transferProphets: OneOrMore<TransferProphet>,
|
transferProphets: OneOrMore<TransferProphet>,
|
||||||
amount: BigNumber,
|
amount: BigNumber,
|
||||||
|
options: {
|
||||||
|
exchangeRates?: readonly BigNumber[]
|
||||||
|
} = {},
|
||||||
): OneOrMore<TransferProphetAppliedResult> => {
|
): OneOrMore<TransferProphetAppliedResult> => {
|
||||||
|
const { exchangeRates } = options
|
||||||
|
|
||||||
|
if (
|
||||||
|
exchangeRates != null &&
|
||||||
|
exchangeRates.length < transferProphets.length - 1
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`[XLinkSDK#applyTransferProphets] exchangeRate count not match with transferProphet count, which is not expected`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return reduce(
|
return reduce(
|
||||||
(acc, transferProphet) =>
|
(acc, transferProphet, idx) =>
|
||||||
concat(acc, [applyTransferProphet(transferProphet, last(acc).netAmount)]),
|
concat(acc, [
|
||||||
[{ fees: [], netAmount: amount }],
|
applyTransferProphet(
|
||||||
transferProphets,
|
transferProphet,
|
||||||
|
BigNumber.mul(
|
||||||
|
last(acc).netAmount,
|
||||||
|
exchangeRates?.[idx] ?? BigNumber.ONE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
[applyTransferProphet(transferProphets[0], amount)],
|
||||||
|
transferProphets.slice(1),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +92,9 @@ export const applyTransferProphet = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* **DO NOT** use this function before we created it's unit tests
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* composeTransferProphets(
|
* composeTransferProphets(
|
||||||
* [
|
* [
|
||||||
@@ -92,6 +117,15 @@ export const composeTransferProphets = (
|
|||||||
transferProphets: readonly TransferProphet[],
|
transferProphets: readonly TransferProphet[],
|
||||||
exchangeRates: readonly BigNumber[],
|
exchangeRates: readonly BigNumber[],
|
||||||
): TransferProphetAggregated<TransferProphet[]> => {
|
): TransferProphetAggregated<TransferProphet[]> => {
|
||||||
|
if (
|
||||||
|
exchangeRates != null &&
|
||||||
|
exchangeRates.length < transferProphets.length - 1
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`[XLinkSDK#composeTransferProphets] exchangeRate count not match with transferProphet count, which is not expected`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const cumulativeExchangeRates = calcCumulativeExchangeRates(exchangeRates)
|
const cumulativeExchangeRates = calcCumulativeExchangeRates(exchangeRates)
|
||||||
|
|
||||||
return reduce(
|
return reduce(
|
||||||
@@ -99,7 +133,7 @@ export const composeTransferProphets = (
|
|||||||
...composeTransferProphet2(
|
...composeTransferProphet2(
|
||||||
res,
|
res,
|
||||||
transferProphet,
|
transferProphet,
|
||||||
cumulativeExchangeRates[idx],
|
cumulativeExchangeRates[idx + 1],
|
||||||
),
|
),
|
||||||
transferProphets: [...res.transferProphets, transferProphet],
|
transferProphets: [...res.transferProphets, transferProphet],
|
||||||
}),
|
}),
|
||||||
@@ -114,18 +148,18 @@ export const composeTransferProphets = (
|
|||||||
/**
|
/**
|
||||||
* @example
|
* @example
|
||||||
* calcCumulativeExchangeRates([
|
* calcCumulativeExchangeRates([
|
||||||
* exchangeRateOf(tokenA, tokenB),
|
* exchangeRateOf(A, B),
|
||||||
* exchangeRateOf(tokenB, tokenC),
|
* exchangeRateOf(B, C),
|
||||||
* exchangeRateOf(tokenC, tokenD),
|
* exchangeRateOf(C, D),
|
||||||
* // ...
|
* // ...
|
||||||
* ])
|
* ])
|
||||||
*
|
*
|
||||||
* // =>
|
* // =>
|
||||||
*
|
*
|
||||||
* [
|
* [
|
||||||
* exchangeRateOf(tokenA, tokenB),
|
* exchangeRateOf(A, B),
|
||||||
* exchangeRateOf(tokenA, tokenC),
|
* exchangeRateOf(A, C),
|
||||||
* exchangeRateOf(tokenA, tokenD),
|
* exchangeRateOf(A, D),
|
||||||
* // ...
|
* // ...
|
||||||
* ]
|
* ]
|
||||||
*/
|
*/
|
||||||
@@ -140,30 +174,6 @@ export const composeTransferProphet2 = (
|
|||||||
transferProphet2: TransferProphet,
|
transferProphet2: TransferProphet,
|
||||||
exchangeRate: BigNumber,
|
exchangeRate: BigNumber,
|
||||||
): TransferProphetAggregated<[TransferProphet, TransferProphet]> => {
|
): TransferProphetAggregated<[TransferProphet, TransferProphet]> => {
|
||||||
if (exchangeRate == null) {
|
|
||||||
throw new Error(
|
|
||||||
"[XLinkSDK#composeTransferProphet2] exchangeRate is required",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const bridgeTokenMinFeeAmount = BigNumber.sum([
|
|
||||||
...transferProphet1.fees.flatMap(f =>
|
|
||||||
f.type === "rate" && f.token === transferProphet1.bridgeToken
|
|
||||||
? [f.minimumAmount]
|
|
||||||
: [],
|
|
||||||
),
|
|
||||||
...transferProphet2.fees.flatMap(f =>
|
|
||||||
f.type === "rate" && f.token === transferProphet2.bridgeToken
|
|
||||||
? [
|
|
||||||
/**
|
|
||||||
* convert to the denomination of the first step token
|
|
||||||
*/
|
|
||||||
BigNumber.div(f.minimumAmount, exchangeRate),
|
|
||||||
]
|
|
||||||
: [],
|
|
||||||
),
|
|
||||||
])
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* flatFeeRate = 1 - (amount1 * (1-feeRate1) * (1-feeRate2) * (1-feeRateN...) / amount1)
|
* flatFeeRate = 1 - (amount1 * (1-feeRate1) * (1-feeRate2) * (1-feeRateN...) / amount1)
|
||||||
* flatFeeRate = 1 - (1-feeRate1) * (1-feeRate2) * (1-feeRateN...)
|
* flatFeeRate = 1 - (1-feeRate1) * (1-feeRate2) * (1-feeRateN...)
|
||||||
@@ -181,15 +191,36 @@ export const composeTransferProphet2 = (
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* step2Amount = step1Amount * (1-feeRate1) * (1-feeRate2) * (1-feeRateN)... * exchangeRate
|
* step2Amount = step1Amount * (1-feeRate1) * (1-feeRate2) * (1-feeRateN)... * exchangeRate
|
||||||
* step2Amount = step1Amount * (1-flatFeeRate) * exchangeRate
|
* step2Amount = step1Amount * (1-flatFeeRate1) * exchangeRate
|
||||||
|
* step2Amount = step1Amount * step1ToStep2Rate
|
||||||
|
* step1ToStep2Rate = (1-flatFeeRate1) * exchangeRate
|
||||||
*/
|
*/
|
||||||
const step1ToStep2Rate = BigNumber.mul(
|
const step1ToStep2Rate = BigNumber.mul(
|
||||||
BigNumber.minus(1, step1FlatFeeRate),
|
BigNumber.minus(1, step1FlatFeeRate),
|
||||||
exchangeRate,
|
exchangeRate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const bridgeTokenMinFeeAmount = BigNumber.sum([
|
||||||
|
...transferProphet1.fees.flatMap(f =>
|
||||||
|
f.type === "rate" && f.token === transferProphet1.bridgeToken
|
||||||
|
? [f.minimumAmount]
|
||||||
|
: [],
|
||||||
|
),
|
||||||
|
...transferProphet2.fees.flatMap(f =>
|
||||||
|
f.type === "rate" && f.token === transferProphet2.bridgeToken
|
||||||
|
? [
|
||||||
|
/**
|
||||||
|
* convert to the denomination of the first step token
|
||||||
|
*/
|
||||||
|
BigNumber.div(f.minimumAmount, step1ToStep2Rate),
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
let minBridgeAmount: BigNumber | null = null
|
let minBridgeAmount: BigNumber | null = null
|
||||||
if (
|
if (
|
||||||
BigNumber.isZero(bridgeTokenMinFeeAmount) /* min fee amount not set */ &&
|
BigNumber.isZero(bridgeTokenMinFeeAmount) /* min fee amount not set */ &&
|
||||||
|
|||||||
1
src/utils/funcHelpers.ts
Normal file
1
src/utils/funcHelpers.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const identity = <T>(x: T): T => x
|
||||||
6
src/utils/objectHelper.ts
Normal file
6
src/utils/objectHelper.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export function entries<T>(obj: T): [keyof T, T[keyof T]][] {
|
||||||
|
return Object.entries(obj as any) as [keyof T, T[keyof T]][]
|
||||||
|
}
|
||||||
|
export function fromEntries<T>(entries: [keyof T, T[keyof T]][]): T {
|
||||||
|
return Object.fromEntries(entries) as T
|
||||||
|
}
|
||||||
@@ -4,12 +4,12 @@ import {
|
|||||||
toSDKNumberOrUndefined,
|
toSDKNumberOrUndefined,
|
||||||
} from "../../xlinkSdkUtils/types"
|
} from "../../xlinkSdkUtils/types"
|
||||||
import { BigNumber } from "../BigNumber"
|
import { BigNumber } from "../BigNumber"
|
||||||
import { last } from "../arrayHelpers"
|
import { first, last } from "../arrayHelpers"
|
||||||
import { KnownRoute } from "../buildSupportedRoutes"
|
import { KnownRoute_WithMetaProtocol } from "../buildSupportedRoutes"
|
||||||
import {
|
import {
|
||||||
applyTransferProphet,
|
applyTransferProphet,
|
||||||
applyTransferProphets,
|
applyTransferProphets,
|
||||||
composeTransferProphets,
|
composeTransferProphet2,
|
||||||
} from "../feeRateHelpers"
|
} from "../feeRateHelpers"
|
||||||
import { checkNever, isNotNull, OneOrMore } from "../typeHelpers"
|
import { checkNever, isNotNull, OneOrMore } from "../typeHelpers"
|
||||||
import { KnownChainId, KnownTokenId } from "./knownIds"
|
import { KnownChainId, KnownTokenId } from "./knownIds"
|
||||||
@@ -98,7 +98,7 @@ export function transformFromPublicTransferProphet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function transformToPublicTransferProphet(
|
export function transformToPublicTransferProphet(
|
||||||
route: KnownRoute,
|
route: KnownRoute_WithMetaProtocol,
|
||||||
fromAmount: SDKNumber | BigNumber,
|
fromAmount: SDKNumber | BigNumber,
|
||||||
transferProphet: TransferProphet,
|
transferProphet: TransferProphet,
|
||||||
): PublicTransferProphet {
|
): PublicTransferProphet {
|
||||||
@@ -155,20 +155,28 @@ export function transformToPublicTransferProphet(
|
|||||||
* ],
|
* ],
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
export const transformToPublicTransferProphetAggregated = (
|
export const transformToPublicTransferProphetAggregated2 = (
|
||||||
transferProphets: OneOrMore<PublicTransferProphet>,
|
routes: [KnownRoute_WithMetaProtocol, KnownRoute_WithMetaProtocol],
|
||||||
exchangeRates: readonly BigNumber[],
|
transferProphets: [TransferProphet, TransferProphet],
|
||||||
): PublicTransferProphetAggregated<OneOrMore<PublicTransferProphet>> => {
|
fromAmount: BigNumber,
|
||||||
const firstTransferProphet = transferProphets[0]
|
exchangeRate: BigNumber,
|
||||||
const lastTransferProphet = last(transferProphets)
|
): PublicTransferProphetAggregated<
|
||||||
|
[PublicTransferProphet, PublicTransferProphet]
|
||||||
|
> => {
|
||||||
|
if (routes.length !== transferProphets.length) {
|
||||||
|
throw new Error(
|
||||||
|
`[XLinkSDK#transformToPublicTransferProphetAggregated2] route count not match with transferProphet count, which is not expected`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const steps = transferProphets.map(
|
const composed = composeTransferProphet2(
|
||||||
transformFromPublicTransferProphet,
|
transferProphets[0],
|
||||||
) as any as OneOrMore<TransferProphet>
|
transferProphets[1],
|
||||||
const composed = composeTransferProphets(steps, exchangeRates)
|
exchangeRate,
|
||||||
|
)
|
||||||
const fromAmount = BigNumber.from(firstTransferProphet.fromAmount)
|
const applyResult = applyTransferProphets(transferProphets, fromAmount, {
|
||||||
const applyResult = applyTransferProphets(steps, fromAmount)
|
exchangeRates: [exchangeRate],
|
||||||
|
})
|
||||||
|
|
||||||
const fees: PublicTransferProphet["fees"] = applyResult
|
const fees: PublicTransferProphet["fees"] = applyResult
|
||||||
.flatMap(r => r.fees)
|
.flatMap(r => r.fees)
|
||||||
@@ -189,17 +197,30 @@ export const transformToPublicTransferProphetAggregated = (
|
|||||||
)
|
)
|
||||||
.filter(isNotNull)
|
.filter(isNotNull)
|
||||||
|
|
||||||
|
const firstRoute = first(routes)
|
||||||
|
const lastRoute = last(routes)
|
||||||
return {
|
return {
|
||||||
fromChain: firstTransferProphet.fromChain,
|
fromChain: firstRoute.fromChain,
|
||||||
fromToken: firstTransferProphet.fromToken,
|
fromToken: firstRoute.fromToken,
|
||||||
toChain: lastTransferProphet.toChain,
|
toChain: lastRoute.toChain,
|
||||||
toToken: lastTransferProphet.toToken,
|
toToken: lastRoute.toToken,
|
||||||
fromAmount: toSDKNumberOrUndefined(fromAmount),
|
fromAmount: toSDKNumberOrUndefined(fromAmount),
|
||||||
toAmount: toSDKNumberOrUndefined(last(applyResult).netAmount),
|
toAmount: toSDKNumberOrUndefined(last(applyResult).netAmount),
|
||||||
isPaused: composed.isPaused,
|
isPaused: composed.isPaused,
|
||||||
fees,
|
fees,
|
||||||
minBridgeAmount: toSDKNumberOrUndefined(composed.minBridgeAmount),
|
minBridgeAmount: toSDKNumberOrUndefined(composed.minBridgeAmount),
|
||||||
maxBridgeAmount: toSDKNumberOrUndefined(composed.maxBridgeAmount),
|
maxBridgeAmount: toSDKNumberOrUndefined(composed.maxBridgeAmount),
|
||||||
transferProphets,
|
transferProphets: [
|
||||||
|
transformToPublicTransferProphet(
|
||||||
|
routes[0],
|
||||||
|
fromAmount,
|
||||||
|
transferProphets[0],
|
||||||
|
),
|
||||||
|
transformToPublicTransferProphet(
|
||||||
|
routes[1],
|
||||||
|
applyResult[1].netAmount,
|
||||||
|
transferProphets[1],
|
||||||
|
),
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,16 +104,21 @@ export namespace KnownTokenId {
|
|||||||
export const vLiSTX = tokenId("stx-vlistx")
|
export const vLiSTX = tokenId("stx-vlistx")
|
||||||
/** Represents the vLiALEX token ID on the Stacks blockchain. */
|
/** Represents the vLiALEX token ID on the Stacks blockchain. */
|
||||||
export const vLiALEX = tokenId("stx-vlialex")
|
export const vLiALEX = tokenId("stx-vlialex")
|
||||||
|
export const vLiaBTC = tokenId("stx-vliabtc")
|
||||||
export const uBTC = tokenId("stx-ubtc")
|
export const uBTC = tokenId("stx-ubtc")
|
||||||
export const DB20 = tokenId("stx-db20")
|
export const DB20 = tokenId("stx-db20")
|
||||||
export const DOG = tokenId("stx-dog")
|
export const DOG = tokenId("stx-dog")
|
||||||
}
|
}
|
||||||
/** This type includes all known tokens on the Stacks blockchain. */
|
export type StacksToken = TokenId<`stx-${string}`>
|
||||||
export type StacksToken = (typeof _allKnownStacksTokens)[number]
|
|
||||||
export function isStacksToken(value: TokenId): value is StacksToken {
|
export function isStacksToken(value: TokenId): value is StacksToken {
|
||||||
return _allKnownStacksTokens.includes(value as any)
|
return value.startsWith("stx-")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export const createStacksToken = (
|
||||||
|
stacksTokenId: string,
|
||||||
|
): KnownTokenId.StacksToken => {
|
||||||
|
return `stx-${stacksTokenId}` as any
|
||||||
|
}
|
||||||
export const createBRC20Token = (
|
export const createBRC20Token = (
|
||||||
brc20tick: string,
|
brc20tick: string,
|
||||||
): KnownTokenId.BRC20Token => {
|
): KnownTokenId.BRC20Token => {
|
||||||
@@ -126,7 +131,6 @@ export const createRunesToken = (
|
|||||||
}
|
}
|
||||||
export const _allKnownBitcoinTokens = Object.values(KnownTokenId.Bitcoin)
|
export const _allKnownBitcoinTokens = Object.values(KnownTokenId.Bitcoin)
|
||||||
export const _allKnownEVMTokens = Object.values(KnownTokenId.EVM)
|
export const _allKnownEVMTokens = Object.values(KnownTokenId.EVM)
|
||||||
export const _allKnownStacksTokens = Object.values(KnownTokenId.Stacks)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `KnownChainId` namespace provides types of blockchain networks
|
* The `KnownChainId` namespace provides types of blockchain networks
|
||||||
|
|||||||
@@ -12,27 +12,27 @@ import {
|
|||||||
prepareTransaction,
|
prepareTransaction,
|
||||||
} from "../bitcoinUtils/prepareTransaction"
|
} from "../bitcoinUtils/prepareTransaction"
|
||||||
import {
|
import {
|
||||||
BridgeSwapRoute_FromBitcoin,
|
|
||||||
CreateBridgeOrderResult,
|
CreateBridgeOrderResult,
|
||||||
createBridgeOrder_BitcoinToEVM,
|
createBridgeOrder_BitcoinToEVM,
|
||||||
|
createBridgeOrder_BitcoinToMeta,
|
||||||
createBridgeOrder_BitcoinToStacks,
|
createBridgeOrder_BitcoinToStacks,
|
||||||
} from "../stacksUtils/createBridgeOrder"
|
} from "../stacksUtils/createBridgeOrderFromBitcoin"
|
||||||
import { validateBridgeOrder } from "../stacksUtils/validateBridgeOrder"
|
import { validateBridgeOrder } from "../stacksUtils/validateBridgeOrder"
|
||||||
import {
|
import { getStacksTokenContractInfo } from "../stacksUtils/xlinkContractHelpers"
|
||||||
getStacksTokenContractInfo,
|
|
||||||
numberToStacksContractNumber,
|
|
||||||
} from "../stacksUtils/xlinkContractHelpers"
|
|
||||||
import { range } from "../utils/arrayHelpers"
|
import { range } from "../utils/arrayHelpers"
|
||||||
import { BigNumber } from "../utils/BigNumber"
|
import { BigNumber } from "../utils/BigNumber"
|
||||||
import {
|
import {
|
||||||
KnownRoute_FromBitcoin,
|
KnownRoute_FromBitcoin,
|
||||||
|
KnownRoute_FromBitcoin_ToBRC20,
|
||||||
KnownRoute_FromBitcoin_ToEVM,
|
KnownRoute_FromBitcoin_ToEVM,
|
||||||
|
KnownRoute_FromBitcoin_ToRunes,
|
||||||
KnownRoute_FromBitcoin_ToStacks,
|
KnownRoute_FromBitcoin_ToStacks,
|
||||||
buildSupportedRoutes,
|
buildSupportedRoutes,
|
||||||
defineRoute,
|
defineRoute,
|
||||||
} from "../utils/buildSupportedRoutes"
|
} from "../utils/buildSupportedRoutes"
|
||||||
import {
|
import {
|
||||||
BridgeValidateFailedError,
|
BridgeValidateFailedError,
|
||||||
|
InvalidMethodParametersError,
|
||||||
UnsupportedBridgeRouteError,
|
UnsupportedBridgeRouteError,
|
||||||
} from "../utils/errors"
|
} from "../utils/errors"
|
||||||
import { decodeHex } from "../utils/hexHelpers"
|
import { decodeHex } from "../utils/hexHelpers"
|
||||||
@@ -42,8 +42,11 @@ import {
|
|||||||
KnownTokenId,
|
KnownTokenId,
|
||||||
_allKnownEVMMainnetChains,
|
_allKnownEVMMainnetChains,
|
||||||
_allKnownEVMTestnetChains,
|
_allKnownEVMTestnetChains,
|
||||||
|
_knownChainIdToErrorMessagePart,
|
||||||
} from "../utils/types/knownIds"
|
} from "../utils/types/knownIds"
|
||||||
import { ChainId, SDKNumber, TokenId } from "./types"
|
import { ChainId, SDKNumber, TokenId } from "./types"
|
||||||
|
import { SwapRoute_WithMinimumAmountsToReceive_Public } from "../utils/SwapRouteHelpers"
|
||||||
|
import { SwapRoute } from "../utils/SwapRouteHelpers"
|
||||||
import { SDKGlobalContext } from "./types.internal"
|
import { SDKGlobalContext } from "./types.internal"
|
||||||
|
|
||||||
export const supportedRoutes = buildSupportedRoutes(
|
export const supportedRoutes = buildSupportedRoutes(
|
||||||
@@ -98,8 +101,13 @@ export interface BridgeFromBitcoinInput {
|
|||||||
fromAddress: string
|
fromAddress: string
|
||||||
fromAddressScriptPubKey: Uint8Array
|
fromAddressScriptPubKey: Uint8Array
|
||||||
toAddress: string
|
toAddress: string
|
||||||
|
/**
|
||||||
|
* **Required** when `toChain` is one of bitcoin chains
|
||||||
|
*/
|
||||||
|
toAddressScriptPubKey?: Uint8Array
|
||||||
amount: SDKNumber
|
amount: SDKNumber
|
||||||
networkFeeRate: bigint
|
networkFeeRate: bigint
|
||||||
|
swapRoute?: SwapRoute_WithMinimumAmountsToReceive_Public
|
||||||
reselectSpendableUTXOs: ReselectSpendableUTXOsFn
|
reselectSpendableUTXOs: ReselectSpendableUTXOsFn
|
||||||
signPsbt: BridgeFromBitcoinInput_signPsbtFn
|
signPsbt: BridgeFromBitcoinInput_signPsbtFn
|
||||||
sendTransaction: (tx: { hex: string }) => Promise<{
|
sendTransaction: (tx: { hex: string }) => Promise<{
|
||||||
@@ -144,13 +152,32 @@ export async function bridgeFromBitcoin(
|
|||||||
toToken: route.toToken,
|
toToken: route.toToken,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (KnownChainId.isBRC20Chain(route.toChain)) {
|
||||||
KnownChainId.isBRC20Chain(route.toChain) ||
|
if (
|
||||||
KnownChainId.isRunesChain(route.toChain)
|
KnownTokenId.isBitcoinToken(route.fromToken) &&
|
||||||
) {
|
KnownTokenId.isBRC20Token(route.toToken)
|
||||||
assertExclude(route.toChain, assertExclude.i<KnownChainId.BRC20Chain>())
|
) {
|
||||||
assertExclude(route.toChain, assertExclude.i<KnownChainId.RunesChain>())
|
return bridgeFromBitcoin_toMeta(ctx, {
|
||||||
// TODO: bitcoin to brc20/runes is not supported yet
|
...info,
|
||||||
|
fromChain: route.fromChain,
|
||||||
|
toChain: route.toChain,
|
||||||
|
fromToken: route.fromToken,
|
||||||
|
toToken: route.toToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (KnownChainId.isRunesChain(route.toChain)) {
|
||||||
|
if (
|
||||||
|
KnownTokenId.isBitcoinToken(route.fromToken) &&
|
||||||
|
KnownTokenId.isRunesToken(route.toToken)
|
||||||
|
) {
|
||||||
|
return bridgeFromBitcoin_toMeta(ctx, {
|
||||||
|
...info,
|
||||||
|
fromChain: route.fromChain,
|
||||||
|
toChain: route.toChain,
|
||||||
|
fromToken: route.fromToken,
|
||||||
|
toToken: route.toToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
assertExclude(route.toChain, assertExclude.i<KnownChainId.BitcoinChain>())
|
assertExclude(route.toChain, assertExclude.i<KnownChainId.BitcoinChain>())
|
||||||
checkNever(route)
|
checkNever(route)
|
||||||
@@ -170,7 +197,7 @@ export async function bridgeFromBitcoin(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function bridgeFromBitcoin_toStacks(
|
async function bridgeFromBitcoin_toStacks(
|
||||||
sdkContext: Pick<SDKGlobalContext, "backendAPI">,
|
sdkContext: SDKGlobalContext,
|
||||||
info: Omit<
|
info: Omit<
|
||||||
BridgeFromBitcoinInput,
|
BridgeFromBitcoinInput,
|
||||||
"fromChain" | "toChain" | "fromToken" | "toToken"
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
@@ -178,7 +205,8 @@ async function bridgeFromBitcoin_toStacks(
|
|||||||
KnownRoute_FromBitcoin_ToStacks,
|
KnownRoute_FromBitcoin_ToStacks,
|
||||||
): Promise<BridgeFromBitcoinOutput> {
|
): Promise<BridgeFromBitcoinOutput> {
|
||||||
const pegInAddress = getBTCPegInAddress(info.fromChain, info.toChain)
|
const pegInAddress = getBTCPegInAddress(info.fromChain, info.toChain)
|
||||||
const toTokenContractInfo = getStacksTokenContractInfo(
|
const toTokenContractInfo = await getStacksTokenContractInfo(
|
||||||
|
sdkContext,
|
||||||
info.toChain,
|
info.toChain,
|
||||||
info.toToken,
|
info.toToken,
|
||||||
)
|
)
|
||||||
@@ -190,14 +218,21 @@ async function bridgeFromBitcoin_toStacks(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdOrder = await createBridgeOrder_BitcoinToStacks({
|
const createdOrder = await createBridgeOrder_BitcoinToStacks(sdkContext, {
|
||||||
fromChain: info.fromChain,
|
fromChain: info.fromChain,
|
||||||
fromBitcoinScriptPubKey: info.fromAddressScriptPubKey,
|
fromBitcoinScriptPubKey: info.fromAddressScriptPubKey,
|
||||||
toChain: info.toChain,
|
toChain: info.toChain,
|
||||||
toToken: info.toToken,
|
toToken: info.toToken,
|
||||||
toStacksAddress: info.toAddress,
|
toStacksAddress: info.toAddress,
|
||||||
swapSlippedAmount: numberToStacksContractNumber(info.amount),
|
swap:
|
||||||
swapRoute: [],
|
info.swapRoute == null
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
...info.swapRoute,
|
||||||
|
minimumAmountsToReceive: BigNumber.from(
|
||||||
|
info.swapRoute.minimumAmountsToReceive,
|
||||||
|
),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if (createdOrder == null) {
|
if (createdOrder == null) {
|
||||||
throw new UnsupportedBridgeRouteError(
|
throw new UnsupportedBridgeRouteError(
|
||||||
@@ -211,21 +246,77 @@ async function bridgeFromBitcoin_toStacks(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function bridgeFromBitcoin_toEVM(
|
async function bridgeFromBitcoin_toEVM(
|
||||||
sdkContext: Pick<SDKGlobalContext, "backendAPI">,
|
sdkContext: SDKGlobalContext,
|
||||||
info: Omit<
|
info: Omit<
|
||||||
BridgeFromBitcoinInput,
|
BridgeFromBitcoinInput,
|
||||||
"fromChain" | "toChain" | "fromToken" | "toToken"
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
> &
|
> &
|
||||||
KnownRoute_FromBitcoin_ToEVM,
|
KnownRoute_FromBitcoin_ToEVM,
|
||||||
): Promise<BridgeFromBitcoinOutput> {
|
): Promise<BridgeFromBitcoinOutput> {
|
||||||
const createdOrder = await createBridgeOrder_BitcoinToEVM({
|
const createdOrder = await createBridgeOrder_BitcoinToEVM(sdkContext, {
|
||||||
fromChain: info.fromChain,
|
fromChain: info.fromChain,
|
||||||
toChain: info.toChain,
|
toChain: info.toChain,
|
||||||
toToken: info.toToken,
|
toToken: info.toToken,
|
||||||
fromBitcoinScriptPubKey: info.fromAddressScriptPubKey,
|
fromBitcoinScriptPubKey: info.fromAddressScriptPubKey,
|
||||||
toEVMAddress: info.toAddress,
|
toEVMAddress: info.toAddress,
|
||||||
swapSlippedAmount: numberToStacksContractNumber(info.amount),
|
swap:
|
||||||
swapRoute: [],
|
info.swapRoute == null
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
...info.swapRoute,
|
||||||
|
minimumAmountsToReceive: BigNumber.from(
|
||||||
|
info.swapRoute.minimumAmountsToReceive,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (createdOrder == null) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
KnownTokenId.Bitcoin.BTC,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return broadcastBitcoinTransaction(sdkContext, info, createdOrder)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bridgeFromBitcoin_toMeta(
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
|
info: Omit<
|
||||||
|
BridgeFromBitcoinInput,
|
||||||
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
|
> &
|
||||||
|
(KnownRoute_FromBitcoin_ToBRC20 | KnownRoute_FromBitcoin_ToRunes),
|
||||||
|
): Promise<BridgeFromBitcoinOutput> {
|
||||||
|
if (info.toAddressScriptPubKey == null) {
|
||||||
|
throw new InvalidMethodParametersError(
|
||||||
|
[
|
||||||
|
"XLinkSDK",
|
||||||
|
`bridgeFromBitcoin (to ${_knownChainIdToErrorMessagePart(info.toChain)})`,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: "toAddressScriptPubKey",
|
||||||
|
expected: "Uint8Array",
|
||||||
|
received: "undefined",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdOrder = await createBridgeOrder_BitcoinToMeta(sdkContext, {
|
||||||
|
...info,
|
||||||
|
fromBitcoinScriptPubKey: info.fromAddressScriptPubKey,
|
||||||
|
toBitcoinScriptPubKey: info.toAddressScriptPubKey,
|
||||||
|
swap:
|
||||||
|
info.swapRoute == null
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
...info.swapRoute,
|
||||||
|
minimumAmountsToReceive: BigNumber.from(
|
||||||
|
info.swapRoute.minimumAmountsToReceive,
|
||||||
|
),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if (createdOrder == null) {
|
if (createdOrder == null) {
|
||||||
throw new UnsupportedBridgeRouteError(
|
throw new UnsupportedBridgeRouteError(
|
||||||
@@ -239,7 +330,7 @@ async function bridgeFromBitcoin_toEVM(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function broadcastBitcoinTransaction(
|
async function broadcastBitcoinTransaction(
|
||||||
sdkContext: Pick<SDKGlobalContext, "backendAPI">,
|
sdkContext: SDKGlobalContext,
|
||||||
info: Omit<
|
info: Omit<
|
||||||
ConstructBitcoinTransactionInput,
|
ConstructBitcoinTransactionInput,
|
||||||
"validateBridgeOrder" | "orderData" | "pegInAddress"
|
"validateBridgeOrder" | "orderData" | "pegInAddress"
|
||||||
@@ -324,11 +415,11 @@ type ConstructBitcoinTransactionInput = PrepareBitcoinTransactionInput & {
|
|||||||
validateBridgeOrder: (
|
validateBridgeOrder: (
|
||||||
pegInTx: Uint8Array,
|
pegInTx: Uint8Array,
|
||||||
revealTx: undefined | Uint8Array,
|
revealTx: undefined | Uint8Array,
|
||||||
swapRoute: BridgeSwapRoute_FromBitcoin,
|
swapRoute?: SwapRoute,
|
||||||
) => Promise<void>
|
) => Promise<void>
|
||||||
}
|
}
|
||||||
async function constructBitcoinTransaction(
|
async function constructBitcoinTransaction(
|
||||||
sdkContext: Pick<SDKGlobalContext, "backendAPI">,
|
sdkContext: SDKGlobalContext,
|
||||||
info: ConstructBitcoinTransactionInput,
|
info: ConstructBitcoinTransactionInput,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
hex: string
|
hex: string
|
||||||
@@ -375,9 +466,16 @@ async function constructBitcoinTransaction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
await info
|
await info
|
||||||
.validateBridgeOrder(signedTx.extract(), revealTx, [])
|
.validateBridgeOrder(signedTx.extract(), revealTx, info.swapRoute)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
throw new BridgeValidateFailedError(err)
|
if (sdkContext.btc.ignoreValidateResult) {
|
||||||
|
console.error(
|
||||||
|
"Bridge tx validation failed, but ignoreValidateResult is true, so we ignore the error",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
throw new BridgeValidateFailedError(err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -391,6 +489,7 @@ export type PrepareBitcoinTransactionInput = KnownRoute_FromBitcoin & {
|
|||||||
fromAddress: BridgeFromBitcoinInput["fromAddress"]
|
fromAddress: BridgeFromBitcoinInput["fromAddress"]
|
||||||
toAddress: BridgeFromBitcoinInput["toAddress"]
|
toAddress: BridgeFromBitcoinInput["toAddress"]
|
||||||
amount: BridgeFromBitcoinInput["amount"]
|
amount: BridgeFromBitcoinInput["amount"]
|
||||||
|
swapRoute?: BridgeFromBitcoinInput["swapRoute"]
|
||||||
networkFeeRate: BridgeFromBitcoinInput["networkFeeRate"]
|
networkFeeRate: BridgeFromBitcoinInput["networkFeeRate"]
|
||||||
reselectSpendableUTXOs: BridgeFromBitcoinInput["reselectSpendableUTXOs"]
|
reselectSpendableUTXOs: BridgeFromBitcoinInput["reselectSpendableUTXOs"]
|
||||||
orderData: Uint8Array
|
orderData: Uint8Array
|
||||||
|
|||||||
@@ -11,10 +11,8 @@ import {
|
|||||||
numberToSolidityContractNumber,
|
numberToSolidityContractNumber,
|
||||||
} from "../evmUtils/xlinkContractHelpers"
|
} from "../evmUtils/xlinkContractHelpers"
|
||||||
import { contractAssignedChainIdFromKnownChain } from "../stacksUtils/crossContractDataMapping"
|
import { contractAssignedChainIdFromKnownChain } from "../stacksUtils/crossContractDataMapping"
|
||||||
import {
|
import { getStacksTokenContractInfo } from "../stacksUtils/xlinkContractHelpers"
|
||||||
addressToBuffer,
|
import { addressToBuffer } from "../utils/addressHelpers"
|
||||||
getStacksTokenContractInfo,
|
|
||||||
} from "../stacksUtils/xlinkContractHelpers"
|
|
||||||
import { BigNumber } from "../utils/BigNumber"
|
import { BigNumber } from "../utils/BigNumber"
|
||||||
import {
|
import {
|
||||||
buildSupportedRoutes,
|
buildSupportedRoutes,
|
||||||
@@ -350,6 +348,7 @@ async function bridgeFromEVM_toStacks(
|
|||||||
info.fromToken,
|
info.fromToken,
|
||||||
)
|
)
|
||||||
const toTokenContractInfo = await getStacksTokenContractInfo(
|
const toTokenContractInfo = await getStacksTokenContractInfo(
|
||||||
|
ctx,
|
||||||
info.toChain,
|
info.toChain,
|
||||||
info.toToken,
|
info.toToken,
|
||||||
)
|
)
|
||||||
@@ -639,7 +638,7 @@ async function bridgeFromEVM_toMeta(
|
|||||||
throw new InvalidMethodParametersError(
|
throw new InvalidMethodParametersError(
|
||||||
[
|
[
|
||||||
"XLinkSDK",
|
"XLinkSDK",
|
||||||
`bridgeFromEVM (to ${KnownChainId.isBRC20Chain(info.toChain) ? "BRC20" : "Runes"})`,
|
`bridgeFromEVM (to ${_knownChainIdToErrorMessagePart(info.toChain)})`,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ import { ContractCallOptions } from "clarity-codegen"
|
|||||||
import { addressToScriptPubKey } from "../bitcoinUtils/bitcoinHelpers"
|
import { addressToScriptPubKey } from "../bitcoinUtils/bitcoinHelpers"
|
||||||
import { contractAssignedChainIdFromKnownChain } from "../stacksUtils/crossContractDataMapping"
|
import { contractAssignedChainIdFromKnownChain } from "../stacksUtils/crossContractDataMapping"
|
||||||
import { isSupportedStacksRoute } from "../stacksUtils/peggingHelpers"
|
import { isSupportedStacksRoute } from "../stacksUtils/peggingHelpers"
|
||||||
|
import {
|
||||||
|
getTerminatingStacksTokenContractAddress,
|
||||||
|
StacksContractName,
|
||||||
|
} from "../stacksUtils/stxContractAddresses"
|
||||||
import {
|
import {
|
||||||
composeTxXLINK,
|
composeTxXLINK,
|
||||||
getStacksContractCallInfo,
|
getStacksContractCallInfo,
|
||||||
@@ -21,14 +25,13 @@ import { UnsupportedBridgeRouteError } from "../utils/errors"
|
|||||||
import { decodeHex } from "../utils/hexHelpers"
|
import { decodeHex } from "../utils/hexHelpers"
|
||||||
import { assertExclude, checkNever } from "../utils/typeHelpers"
|
import { assertExclude, checkNever } from "../utils/typeHelpers"
|
||||||
import {
|
import {
|
||||||
KnownChainId,
|
|
||||||
KnownTokenId,
|
|
||||||
_allKnownEVMMainnetChains,
|
_allKnownEVMMainnetChains,
|
||||||
_allKnownEVMTestnetChains,
|
_allKnownEVMTestnetChains,
|
||||||
|
KnownChainId,
|
||||||
|
KnownTokenId,
|
||||||
} from "../utils/types/knownIds"
|
} from "../utils/types/knownIds"
|
||||||
import { ChainId, SDKNumber, TokenId } from "./types"
|
import { ChainId, SDKNumber, TokenId } from "./types"
|
||||||
import { SDKGlobalContext } from "./types.internal"
|
import { SDKGlobalContext } from "./types.internal"
|
||||||
import { getTerminatingStacksTokenContractAddress } from "../stacksUtils/stxContractAddresses"
|
|
||||||
|
|
||||||
export const supportedRoutes = buildSupportedRoutes(
|
export const supportedRoutes = buildSupportedRoutes(
|
||||||
[
|
[
|
||||||
@@ -139,7 +142,7 @@ export async function bridgeFromStacks(
|
|||||||
KnownTokenId.isStacksToken(route.fromToken) &&
|
KnownTokenId.isStacksToken(route.fromToken) &&
|
||||||
KnownTokenId.isEVMToken(route.toToken)
|
KnownTokenId.isEVMToken(route.toToken)
|
||||||
) {
|
) {
|
||||||
return bridgeFromStacks_toEVM({
|
return bridgeFromStacks_toEVM(ctx, {
|
||||||
...info,
|
...info,
|
||||||
fromChain: route.fromChain,
|
fromChain: route.fromChain,
|
||||||
toChain: route.toChain,
|
toChain: route.toChain,
|
||||||
@@ -152,7 +155,7 @@ export async function bridgeFromStacks(
|
|||||||
KnownTokenId.isStacksToken(route.fromToken) &&
|
KnownTokenId.isStacksToken(route.fromToken) &&
|
||||||
KnownTokenId.isBRC20Token(route.toToken)
|
KnownTokenId.isBRC20Token(route.toToken)
|
||||||
) {
|
) {
|
||||||
return bridgeFromStacks_toMeta({
|
return bridgeFromStacks_toMeta(ctx, {
|
||||||
...info,
|
...info,
|
||||||
fromChain: route.fromChain,
|
fromChain: route.fromChain,
|
||||||
toChain: route.toChain,
|
toChain: route.toChain,
|
||||||
@@ -165,7 +168,7 @@ export async function bridgeFromStacks(
|
|||||||
KnownTokenId.isStacksToken(route.fromToken) &&
|
KnownTokenId.isStacksToken(route.fromToken) &&
|
||||||
KnownTokenId.isRunesToken(route.toToken)
|
KnownTokenId.isRunesToken(route.toToken)
|
||||||
) {
|
) {
|
||||||
return bridgeFromStacks_toMeta({
|
return bridgeFromStacks_toMeta(ctx, {
|
||||||
...info,
|
...info,
|
||||||
fromChain: route.fromChain,
|
fromChain: route.fromChain,
|
||||||
toChain: route.toChain,
|
toChain: route.toChain,
|
||||||
@@ -200,7 +203,7 @@ async function bridgeFromStacks_toBitcoin(
|
|||||||
): Promise<BridgeFromStacksOutput> {
|
): Promise<BridgeFromStacksOutput> {
|
||||||
const contractCallInfo = getStacksContractCallInfo(
|
const contractCallInfo = getStacksContractCallInfo(
|
||||||
info.fromChain,
|
info.fromChain,
|
||||||
"btc-peg-out-endpoint-v2-01",
|
StacksContractName.BTCPegOutEndpoint,
|
||||||
)
|
)
|
||||||
if (!contractCallInfo) {
|
if (!contractCallInfo) {
|
||||||
throw new UnsupportedBridgeRouteError(
|
throw new UnsupportedBridgeRouteError(
|
||||||
@@ -223,13 +226,14 @@ async function bridgeFromStacks_toBitcoin(
|
|||||||
"peg-out-address": addressToScriptPubKey(bitcoinNetwork, info.toAddress),
|
"peg-out-address": addressToScriptPubKey(bitcoinNetwork, info.toAddress),
|
||||||
amount: numberToStacksContractNumber(info.amount),
|
amount: numberToStacksContractNumber(info.amount),
|
||||||
},
|
},
|
||||||
{ deployerAddress: contractCallInfo.deployerAddress },
|
contractCallInfo.executeOptions,
|
||||||
)
|
)
|
||||||
|
|
||||||
return await info.sendTransaction(options)
|
return await info.sendTransaction(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bridgeFromStacks_toEVM(
|
async function bridgeFromStacks_toEVM(
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
info: Omit<
|
info: Omit<
|
||||||
BridgeFromStacksInput,
|
BridgeFromStacksInput,
|
||||||
"fromChain" | "toChain" | "fromToken" | "toToken"
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
@@ -238,9 +242,10 @@ async function bridgeFromStacks_toEVM(
|
|||||||
): Promise<BridgeFromStacksOutput> {
|
): Promise<BridgeFromStacksOutput> {
|
||||||
const contractCallInfo = getStacksContractCallInfo(
|
const contractCallInfo = getStacksContractCallInfo(
|
||||||
info.fromChain,
|
info.fromChain,
|
||||||
"cross-peg-out-endpoint-v2-01",
|
StacksContractName.EVMPegOutEndpoint,
|
||||||
)
|
)
|
||||||
const fromTokenContractInfo = getStacksTokenContractInfo(
|
const fromTokenContractInfo = await getStacksTokenContractInfo(
|
||||||
|
ctx,
|
||||||
info.fromChain,
|
info.fromChain,
|
||||||
info.fromToken,
|
info.fromToken,
|
||||||
)
|
)
|
||||||
@@ -265,13 +270,14 @@ async function bridgeFromStacks_toEVM(
|
|||||||
"dest-chain-id": contractAssignedChainIdFromKnownChain(info.toChain),
|
"dest-chain-id": contractAssignedChainIdFromKnownChain(info.toChain),
|
||||||
"settle-address": decodeHex(info.toAddress),
|
"settle-address": decodeHex(info.toAddress),
|
||||||
},
|
},
|
||||||
{ deployerAddress: contractCallInfo.deployerAddress },
|
contractCallInfo.executeOptions,
|
||||||
)
|
)
|
||||||
|
|
||||||
return await info.sendTransaction(options)
|
return await info.sendTransaction(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bridgeFromStacks_toMeta(
|
async function bridgeFromStacks_toMeta(
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
info: Omit<
|
info: Omit<
|
||||||
BridgeFromStacksInput,
|
BridgeFromStacksInput,
|
||||||
"fromChain" | "toChain" | "fromToken" | "toToken"
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
@@ -280,9 +286,10 @@ async function bridgeFromStacks_toMeta(
|
|||||||
): Promise<BridgeFromStacksOutput> {
|
): Promise<BridgeFromStacksOutput> {
|
||||||
const contractCallInfo = getStacksContractCallInfo(
|
const contractCallInfo = getStacksContractCallInfo(
|
||||||
info.fromChain,
|
info.fromChain,
|
||||||
"meta-peg-out-endpoint-v2-04",
|
StacksContractName.MetaPegOutEndpoint,
|
||||||
)
|
)
|
||||||
const fromTokenContractInfo = getStacksTokenContractInfo(
|
const fromTokenContractInfo = await getStacksTokenContractInfo(
|
||||||
|
ctx,
|
||||||
info.fromChain,
|
info.fromChain,
|
||||||
info.fromToken,
|
info.fromToken,
|
||||||
)
|
)
|
||||||
@@ -310,7 +317,7 @@ async function bridgeFromStacks_toMeta(
|
|||||||
"token-trait": `${fromTokenContractInfo.deployerAddress}.${fromTokenContractInfo.contractName}`,
|
"token-trait": `${fromTokenContractInfo.deployerAddress}.${fromTokenContractInfo.contractName}`,
|
||||||
amount: numberToStacksContractNumber(info.amount),
|
amount: numberToStacksContractNumber(info.amount),
|
||||||
},
|
},
|
||||||
{ deployerAddress: contractCallInfo.deployerAddress },
|
contractCallInfo.executeOptions,
|
||||||
)
|
)
|
||||||
|
|
||||||
return await info.sendTransaction(options)
|
return await info.sendTransaction(options)
|
||||||
|
|||||||
@@ -1,17 +1,26 @@
|
|||||||
import { getBtc2StacksFeeInfo } from "../bitcoinUtils/peggingHelpers"
|
import { getBtc2StacksFeeInfo } from "../bitcoinUtils/peggingHelpers"
|
||||||
import { getStacks2EvmFeeInfo } from "../evmUtils/peggingHelpers"
|
import { getStacks2EvmFeeInfo } from "../evmUtils/peggingHelpers"
|
||||||
|
import { getStacks2MetaFeeInfo } from "../metaUtils/peggingHelpers"
|
||||||
import { BigNumber } from "../utils/BigNumber"
|
import { BigNumber } from "../utils/BigNumber"
|
||||||
|
import {
|
||||||
|
getTransitStacksChainTransitStepInfos,
|
||||||
|
SwapRoute_WithExchangeRate_Public,
|
||||||
|
} from "../utils/SwapRouteHelpers"
|
||||||
import {
|
import {
|
||||||
KnownRoute,
|
KnownRoute,
|
||||||
|
KnownRoute_FromBitcoin_ToBRC20,
|
||||||
KnownRoute_FromBitcoin_ToEVM,
|
KnownRoute_FromBitcoin_ToEVM,
|
||||||
|
KnownRoute_FromBitcoin_ToRunes,
|
||||||
KnownRoute_FromBitcoin_ToStacks,
|
KnownRoute_FromBitcoin_ToStacks,
|
||||||
|
KnownRoute_FromStacks_ToBRC20,
|
||||||
|
KnownRoute_FromStacks_ToRunes,
|
||||||
} from "../utils/buildSupportedRoutes"
|
} from "../utils/buildSupportedRoutes"
|
||||||
import { UnsupportedBridgeRouteError } from "../utils/errors"
|
import { UnsupportedBridgeRouteError } from "../utils/errors"
|
||||||
import { assertExclude, checkNever } from "../utils/typeHelpers"
|
import { assertExclude, checkNever } from "../utils/typeHelpers"
|
||||||
import {
|
import {
|
||||||
PublicTransferProphetAggregated,
|
PublicTransferProphetAggregated,
|
||||||
transformToPublicTransferProphet,
|
transformToPublicTransferProphet,
|
||||||
transformToPublicTransferProphetAggregated,
|
transformToPublicTransferProphetAggregated2,
|
||||||
} from "../utils/types/TransferProphet"
|
} from "../utils/types/TransferProphet"
|
||||||
import { KnownChainId, KnownTokenId } from "../utils/types/knownIds"
|
import { KnownChainId, KnownTokenId } from "../utils/types/knownIds"
|
||||||
import { supportedRoutes } from "./bridgeFromBitcoin"
|
import { supportedRoutes } from "./bridgeFromBitcoin"
|
||||||
@@ -24,6 +33,7 @@ export interface BridgeInfoFromBitcoinInput {
|
|||||||
fromToken: TokenId
|
fromToken: TokenId
|
||||||
toToken: TokenId
|
toToken: TokenId
|
||||||
amount: SDKNumber
|
amount: SDKNumber
|
||||||
|
swapRoute?: SwapRoute_WithExchangeRate_Public
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BridgeInfoFromBitcoinOutput
|
export interface BridgeInfoFromBitcoinOutput
|
||||||
@@ -54,7 +64,33 @@ export const bridgeInfoFromBitcoin = async (
|
|||||||
KnownTokenId.isBitcoinToken(route.fromToken) &&
|
KnownTokenId.isBitcoinToken(route.fromToken) &&
|
||||||
KnownTokenId.isEVMToken(route.toToken)
|
KnownTokenId.isEVMToken(route.toToken)
|
||||||
) {
|
) {
|
||||||
return bridgeInfoFromBitcoin_toEVM({
|
return bridgeInfoFromBitcoin_toEVM(ctx, {
|
||||||
|
...info,
|
||||||
|
fromChain: route.fromChain,
|
||||||
|
toChain: route.toChain,
|
||||||
|
fromToken: route.fromToken,
|
||||||
|
toToken: route.toToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (KnownChainId.isBRC20Chain(route.toChain)) {
|
||||||
|
if (
|
||||||
|
KnownTokenId.isBitcoinToken(route.fromToken) &&
|
||||||
|
KnownTokenId.isBRC20Token(route.toToken)
|
||||||
|
) {
|
||||||
|
return bridgeInfoFromBitcoin_toMeta(ctx, {
|
||||||
|
...info,
|
||||||
|
fromChain: route.fromChain,
|
||||||
|
toChain: route.toChain,
|
||||||
|
fromToken: route.fromToken,
|
||||||
|
toToken: route.toToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (KnownChainId.isRunesChain(route.toChain)) {
|
||||||
|
if (
|
||||||
|
KnownTokenId.isBitcoinToken(route.fromToken) &&
|
||||||
|
KnownTokenId.isRunesToken(route.toToken)
|
||||||
|
) {
|
||||||
|
return bridgeInfoFromBitcoin_toMeta(ctx, {
|
||||||
...info,
|
...info,
|
||||||
fromChain: route.fromChain,
|
fromChain: route.fromChain,
|
||||||
toChain: route.toChain,
|
toChain: route.toChain,
|
||||||
@@ -62,13 +98,6 @@ export const bridgeInfoFromBitcoin = async (
|
|||||||
toToken: route.toToken,
|
toToken: route.toToken,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (
|
|
||||||
KnownChainId.isBRC20Chain(route.toChain) ||
|
|
||||||
KnownChainId.isRunesChain(route.toChain)
|
|
||||||
) {
|
|
||||||
assertExclude(route.toChain, assertExclude.i<KnownChainId.BRC20Chain>())
|
|
||||||
assertExclude(route.toChain, assertExclude.i<KnownChainId.RunesChain>())
|
|
||||||
// TODO: bitcoin to brc20/runes is not supported yet
|
|
||||||
} else {
|
} else {
|
||||||
assertExclude(route.toChain, assertExclude.i<KnownChainId.BitcoinChain>())
|
assertExclude(route.toChain, assertExclude.i<KnownChainId.BitcoinChain>())
|
||||||
checkNever(route)
|
checkNever(route)
|
||||||
@@ -94,7 +123,9 @@ async function bridgeInfoFromBitcoin_toStacks(
|
|||||||
> &
|
> &
|
||||||
KnownRoute_FromBitcoin_ToStacks,
|
KnownRoute_FromBitcoin_ToStacks,
|
||||||
): Promise<BridgeInfoFromBitcoinOutput> {
|
): Promise<BridgeInfoFromBitcoinOutput> {
|
||||||
const step1 = await getBtc2StacksFeeInfo(info)
|
const step1 = await getBtc2StacksFeeInfo(info, {
|
||||||
|
swapRoute: info.swapRoute ?? null,
|
||||||
|
})
|
||||||
if (step1 == null) {
|
if (step1 == null) {
|
||||||
throw new UnsupportedBridgeRouteError(
|
throw new UnsupportedBridgeRouteError(
|
||||||
info.fromChain,
|
info.fromChain,
|
||||||
@@ -111,6 +142,7 @@ async function bridgeInfoFromBitcoin_toStacks(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function bridgeInfoFromBitcoin_toEVM(
|
async function bridgeInfoFromBitcoin_toEVM(
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
info: Omit<
|
info: Omit<
|
||||||
BridgeInfoFromBitcoinInput,
|
BridgeInfoFromBitcoinInput,
|
||||||
"fromChain" | "toChain" | "fromToken" | "toToken"
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
@@ -135,9 +167,21 @@ async function bridgeInfoFromBitcoin_toEVM(
|
|||||||
toToken: info.toToken,
|
toToken: info.toToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: add support for Bitcoin -> EVM with swap
|
||||||
|
if (info.swapRoute != null) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
info.fromToken,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const [step1, step2] = await Promise.all([
|
const [step1, step2] = await Promise.all([
|
||||||
getBtc2StacksFeeInfo(step1Route),
|
getBtc2StacksFeeInfo(step1Route, {
|
||||||
getStacks2EvmFeeInfo(step2Route),
|
swapRoute: info.swapRoute ?? null,
|
||||||
|
}),
|
||||||
|
getStacks2EvmFeeInfo(ctx, step2Route),
|
||||||
])
|
])
|
||||||
if (step1 == null || step2 == null) {
|
if (step1 == null || step2 == null) {
|
||||||
throw new UnsupportedBridgeRouteError(
|
throw new UnsupportedBridgeRouteError(
|
||||||
@@ -148,22 +192,64 @@ async function bridgeInfoFromBitcoin_toEVM(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const step1TransferProphet = transformToPublicTransferProphet(
|
return transformToPublicTransferProphetAggregated2(
|
||||||
step1Route,
|
[step1Route, step2Route],
|
||||||
info.amount,
|
[step1, step2],
|
||||||
step1,
|
BigNumber.from(info.amount),
|
||||||
|
BigNumber.ONE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bridgeInfoFromBitcoin_toMeta(
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
|
info: Omit<
|
||||||
|
BridgeInfoFromBitcoinInput,
|
||||||
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
|
> &
|
||||||
|
(KnownRoute_FromBitcoin_ToBRC20 | KnownRoute_FromBitcoin_ToRunes),
|
||||||
|
): Promise<BridgeInfoFromBitcoinOutput> {
|
||||||
|
const transitStacksChain =
|
||||||
|
info.fromChain === KnownChainId.Bitcoin.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet
|
||||||
|
|
||||||
|
const { step1ToStacksToken, step2FromStacksToken } =
|
||||||
|
await getTransitStacksChainTransitStepInfos(ctx, info)
|
||||||
|
|
||||||
|
const step1Route: KnownRoute = {
|
||||||
|
fromChain: info.fromChain,
|
||||||
|
fromToken: info.fromToken,
|
||||||
|
toChain: transitStacksChain,
|
||||||
|
toToken: step1ToStacksToken,
|
||||||
|
}
|
||||||
|
const step2Route:
|
||||||
|
| KnownRoute_FromStacks_ToBRC20
|
||||||
|
| KnownRoute_FromStacks_ToRunes = {
|
||||||
|
fromChain: transitStacksChain,
|
||||||
|
fromToken: step2FromStacksToken,
|
||||||
|
toChain: info.toChain as any,
|
||||||
|
toToken: info.toToken as any,
|
||||||
|
}
|
||||||
|
|
||||||
|
const [step1, step2] = await Promise.all([
|
||||||
|
getBtc2StacksFeeInfo(step1Route, {
|
||||||
|
swapRoute: info.swapRoute ?? null,
|
||||||
|
}),
|
||||||
|
getStacks2MetaFeeInfo(ctx, step2Route),
|
||||||
|
])
|
||||||
|
if (step1 == null || step2 == null) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
info.fromToken,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformToPublicTransferProphetAggregated2(
|
||||||
|
[step1Route, step2Route],
|
||||||
|
[step1, step2],
|
||||||
|
BigNumber.from(info.amount),
|
||||||
|
BigNumber.from(info.swapRoute?.composedExchangeRate ?? BigNumber.ONE),
|
||||||
)
|
)
|
||||||
const step2TransferProphet = transformToPublicTransferProphet(
|
|
||||||
step2Route,
|
|
||||||
step1TransferProphet.toAmount,
|
|
||||||
step2,
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
...transformToPublicTransferProphetAggregated(
|
|
||||||
[step1TransferProphet, step2TransferProphet],
|
|
||||||
[BigNumber.ONE],
|
|
||||||
),
|
|
||||||
transferProphets: [step1TransferProphet, step2TransferProphet],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { nativeCurrencyAddress } from "../evmUtils/addressHelpers"
|
|||||||
import {
|
import {
|
||||||
getEvm2StacksFeeInfo,
|
getEvm2StacksFeeInfo,
|
||||||
getStacks2EvmFeeInfo,
|
getStacks2EvmFeeInfo,
|
||||||
toCorrespondingStacksToken,
|
evmTokenToCorrespondingStacksToken,
|
||||||
} from "../evmUtils/peggingHelpers"
|
} from "../evmUtils/peggingHelpers"
|
||||||
import { getEVMTokenContractInfo } from "../evmUtils/xlinkContractHelpers"
|
import { getEVMTokenContractInfo } from "../evmUtils/xlinkContractHelpers"
|
||||||
import { getStacks2MetaFeeInfo } from "../metaUtils/peggingHelpers"
|
import { getStacks2MetaFeeInfo } from "../metaUtils/peggingHelpers"
|
||||||
@@ -23,7 +23,7 @@ import { assertExclude, checkNever } from "../utils/typeHelpers"
|
|||||||
import {
|
import {
|
||||||
PublicTransferProphetAggregated,
|
PublicTransferProphetAggregated,
|
||||||
transformToPublicTransferProphet,
|
transformToPublicTransferProphet,
|
||||||
transformToPublicTransferProphetAggregated,
|
transformToPublicTransferProphetAggregated2,
|
||||||
} from "../utils/types/TransferProphet"
|
} from "../utils/types/TransferProphet"
|
||||||
import { KnownChainId, KnownTokenId } from "../utils/types/knownIds"
|
import { KnownChainId, KnownTokenId } from "../utils/types/knownIds"
|
||||||
import { supportedRoutes } from "./bridgeFromEVM"
|
import { supportedRoutes } from "./bridgeFromEVM"
|
||||||
@@ -173,7 +173,9 @@ async function bridgeInfoFromEVM_toBitcoin(
|
|||||||
const transitStacksChain = KnownChainId.isEVMMainnetChain(info.fromChain)
|
const transitStacksChain = KnownChainId.isEVMMainnetChain(info.fromChain)
|
||||||
? KnownChainId.Stacks.Mainnet
|
? KnownChainId.Stacks.Mainnet
|
||||||
: KnownChainId.Stacks.Testnet
|
: KnownChainId.Stacks.Testnet
|
||||||
const transitStacksToken = await toCorrespondingStacksToken(info.fromToken)
|
const transitStacksToken = await evmTokenToCorrespondingStacksToken(
|
||||||
|
info.fromToken,
|
||||||
|
)
|
||||||
if (transitStacksToken == null) {
|
if (transitStacksToken == null) {
|
||||||
throw new UnsupportedBridgeRouteError(
|
throw new UnsupportedBridgeRouteError(
|
||||||
info.fromChain,
|
info.fromChain,
|
||||||
@@ -198,7 +200,9 @@ async function bridgeInfoFromEVM_toBitcoin(
|
|||||||
|
|
||||||
const [step1, step2] = await Promise.all([
|
const [step1, step2] = await Promise.all([
|
||||||
getEvm2StacksFeeInfo(ctx, step1Route),
|
getEvm2StacksFeeInfo(ctx, step1Route),
|
||||||
getStacks2BtcFeeInfo(step2Route),
|
getStacks2BtcFeeInfo(step2Route, {
|
||||||
|
swappedFromRoute: step1Route,
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
if (step1 == null || step2 == null) {
|
if (step1 == null || step2 == null) {
|
||||||
throw new UnsupportedBridgeRouteError(
|
throw new UnsupportedBridgeRouteError(
|
||||||
@@ -209,24 +213,12 @@ async function bridgeInfoFromEVM_toBitcoin(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const step1TransferProphet = transformToPublicTransferProphet(
|
return transformToPublicTransferProphetAggregated2(
|
||||||
step1Route,
|
[step1Route, step2Route],
|
||||||
info.amount,
|
[step1, step2],
|
||||||
step1,
|
BigNumber.from(info.amount),
|
||||||
|
BigNumber.ONE,
|
||||||
)
|
)
|
||||||
const step2TransferProphet = transformToPublicTransferProphet(
|
|
||||||
step2Route,
|
|
||||||
step1TransferProphet.toAmount,
|
|
||||||
step2,
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
...transformToPublicTransferProphetAggregated(
|
|
||||||
[step1TransferProphet, step2TransferProphet],
|
|
||||||
[BigNumber.ONE],
|
|
||||||
),
|
|
||||||
transferProphets: [step1TransferProphet, step2TransferProphet],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bridgeInfoFromEVM_toEVM(
|
async function bridgeInfoFromEVM_toEVM(
|
||||||
@@ -246,7 +238,9 @@ async function bridgeInfoFromEVM_toEVM(
|
|||||||
const transitStacksChain = KnownChainId.isEVMMainnetChain(info.fromChain)
|
const transitStacksChain = KnownChainId.isEVMMainnetChain(info.fromChain)
|
||||||
? KnownChainId.Stacks.Mainnet
|
? KnownChainId.Stacks.Mainnet
|
||||||
: KnownChainId.Stacks.Testnet
|
: KnownChainId.Stacks.Testnet
|
||||||
const transitStacksToken = await toCorrespondingStacksToken(info.fromToken)
|
const transitStacksToken = await evmTokenToCorrespondingStacksToken(
|
||||||
|
info.fromToken,
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
transitStacksToken == null ||
|
transitStacksToken == null ||
|
||||||
evmTokenContractCallInfo?.tokenContractAddress === nativeCurrencyAddress
|
evmTokenContractCallInfo?.tokenContractAddress === nativeCurrencyAddress
|
||||||
@@ -274,7 +268,7 @@ async function bridgeInfoFromEVM_toEVM(
|
|||||||
|
|
||||||
const [step1, step2] = await Promise.all([
|
const [step1, step2] = await Promise.all([
|
||||||
getEvm2StacksFeeInfo(ctx, step1Route),
|
getEvm2StacksFeeInfo(ctx, step1Route),
|
||||||
getStacks2EvmFeeInfo(step2Route),
|
getStacks2EvmFeeInfo(ctx, step2Route),
|
||||||
])
|
])
|
||||||
if (step1 == null || step2 == null) {
|
if (step1 == null || step2 == null) {
|
||||||
throw new UnsupportedBridgeRouteError(
|
throw new UnsupportedBridgeRouteError(
|
||||||
@@ -285,24 +279,12 @@ async function bridgeInfoFromEVM_toEVM(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const step1TransferProphet = transformToPublicTransferProphet(
|
return transformToPublicTransferProphetAggregated2(
|
||||||
step1Route,
|
[step1Route, step2Route],
|
||||||
info.amount,
|
[step1, step2],
|
||||||
step1,
|
BigNumber.from(info.amount),
|
||||||
|
BigNumber.ONE,
|
||||||
)
|
)
|
||||||
const step2TransferProphet = transformToPublicTransferProphet(
|
|
||||||
step2Route,
|
|
||||||
step1TransferProphet.toAmount,
|
|
||||||
step2,
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
...transformToPublicTransferProphetAggregated(
|
|
||||||
[step1TransferProphet, step2TransferProphet],
|
|
||||||
[BigNumber.ONE],
|
|
||||||
),
|
|
||||||
transferProphets: [step1TransferProphet, step2TransferProphet],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bridgeInfoFromEVM_toMeta(
|
async function bridgeInfoFromEVM_toMeta(
|
||||||
@@ -322,7 +304,9 @@ async function bridgeInfoFromEVM_toMeta(
|
|||||||
const transitStacksChain = KnownChainId.isEVMMainnetChain(info.fromChain)
|
const transitStacksChain = KnownChainId.isEVMMainnetChain(info.fromChain)
|
||||||
? KnownChainId.Stacks.Mainnet
|
? KnownChainId.Stacks.Mainnet
|
||||||
: KnownChainId.Stacks.Testnet
|
: KnownChainId.Stacks.Testnet
|
||||||
const transitStacksToken = await toCorrespondingStacksToken(info.fromToken)
|
const transitStacksToken = await evmTokenToCorrespondingStacksToken(
|
||||||
|
info.fromToken,
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
transitStacksToken == null ||
|
transitStacksToken == null ||
|
||||||
evmTokenContractCallInfo?.tokenContractAddress === nativeCurrencyAddress
|
evmTokenContractCallInfo?.tokenContractAddress === nativeCurrencyAddress
|
||||||
@@ -363,22 +347,10 @@ async function bridgeInfoFromEVM_toMeta(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const step1TransferProphet = transformToPublicTransferProphet(
|
return transformToPublicTransferProphetAggregated2(
|
||||||
step1Route,
|
[step1Route, step2Route],
|
||||||
info.amount,
|
[step1, step2],
|
||||||
step1,
|
BigNumber.from(info.amount),
|
||||||
|
BigNumber.ONE,
|
||||||
)
|
)
|
||||||
const step2TransferProphet = transformToPublicTransferProphet(
|
|
||||||
step2Route,
|
|
||||||
step1TransferProphet.toAmount,
|
|
||||||
step2,
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
...transformToPublicTransferProphetAggregated(
|
|
||||||
[step1TransferProphet, step2TransferProphet],
|
|
||||||
[BigNumber.ONE],
|
|
||||||
),
|
|
||||||
transferProphets: [step1TransferProphet, step2TransferProphet],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
354
src/xlinkSdkUtils/bridgeInfoFromMeta.ts
Normal file
354
src/xlinkSdkUtils/bridgeInfoFromMeta.ts
Normal file
@@ -0,0 +1,354 @@
|
|||||||
|
import { getStacks2BtcFeeInfo } from "../bitcoinUtils/peggingHelpers"
|
||||||
|
import { getStacks2EvmFeeInfo } from "../evmUtils/peggingHelpers"
|
||||||
|
import {
|
||||||
|
getMeta2StacksFeeInfo,
|
||||||
|
getStacks2MetaFeeInfo,
|
||||||
|
} from "../metaUtils/peggingHelpers"
|
||||||
|
import { BigNumber } from "../utils/BigNumber"
|
||||||
|
import {
|
||||||
|
getTransitStacksChainTransitStepInfos,
|
||||||
|
SwapRoute_WithExchangeRate_Public,
|
||||||
|
} from "../utils/SwapRouteHelpers"
|
||||||
|
import {
|
||||||
|
KnownRoute,
|
||||||
|
KnownRoute_FromBRC20_ToBitcoin,
|
||||||
|
KnownRoute_FromBRC20_ToBRC20,
|
||||||
|
KnownRoute_FromBRC20_ToEVM,
|
||||||
|
KnownRoute_FromBRC20_ToRunes,
|
||||||
|
KnownRoute_FromBRC20_ToStacks,
|
||||||
|
KnownRoute_FromRunes_ToBitcoin,
|
||||||
|
KnownRoute_FromRunes_ToBRC20,
|
||||||
|
KnownRoute_FromRunes_ToEVM,
|
||||||
|
KnownRoute_FromRunes_ToRunes,
|
||||||
|
KnownRoute_FromRunes_ToStacks,
|
||||||
|
KnownRoute_FromStacks_ToBRC20,
|
||||||
|
KnownRoute_FromStacks_ToRunes,
|
||||||
|
KnownRoute_WithMetaProtocol,
|
||||||
|
} from "../utils/buildSupportedRoutes"
|
||||||
|
import { UnsupportedBridgeRouteError } from "../utils/errors"
|
||||||
|
import { assertExclude, checkNever } from "../utils/typeHelpers"
|
||||||
|
import {
|
||||||
|
PublicTransferProphetAggregated,
|
||||||
|
transformToPublicTransferProphet,
|
||||||
|
transformToPublicTransferProphetAggregated2,
|
||||||
|
} from "../utils/types/TransferProphet"
|
||||||
|
import { KnownChainId, KnownTokenId } from "../utils/types/knownIds"
|
||||||
|
import { ChainId, SDKNumber, TokenId } from "./types"
|
||||||
|
import { SDKGlobalContext } from "./types.internal"
|
||||||
|
|
||||||
|
export interface BridgeInfoFromMetaInput {
|
||||||
|
fromChain: ChainId
|
||||||
|
toChain: ChainId
|
||||||
|
fromToken: TokenId
|
||||||
|
toToken: TokenId
|
||||||
|
amount: SDKNumber
|
||||||
|
swapRoute?: SwapRoute_WithExchangeRate_Public
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BridgeInfoFromMetaOutput
|
||||||
|
extends PublicTransferProphetAggregated {}
|
||||||
|
|
||||||
|
export const bridgeInfoFromMeta = async (
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
|
info: BridgeInfoFromMetaInput,
|
||||||
|
): Promise<BridgeInfoFromMetaOutput> => {
|
||||||
|
const isMetaProtocolChain = (
|
||||||
|
chain: ChainId,
|
||||||
|
): chain is KnownChainId.BRC20Chain | KnownChainId.RunesChain =>
|
||||||
|
KnownChainId.isBRC20Chain(chain) || KnownChainId.isRunesChain(chain)
|
||||||
|
const isMetaProtocolToken = (
|
||||||
|
token: TokenId,
|
||||||
|
): token is KnownTokenId.BRC20Token | KnownTokenId.RunesToken =>
|
||||||
|
KnownTokenId.isBRC20Token(token) || KnownTokenId.isRunesToken(token)
|
||||||
|
|
||||||
|
const route = info as any as KnownRoute_WithMetaProtocol
|
||||||
|
|
||||||
|
if (isMetaProtocolChain(route.fromChain)) {
|
||||||
|
if (KnownChainId.isStacksChain(route.toChain)) {
|
||||||
|
if (
|
||||||
|
isMetaProtocolToken(route.fromToken) &&
|
||||||
|
KnownTokenId.isStacksToken(route.toToken)
|
||||||
|
) {
|
||||||
|
return bridgeInfoFromMeta_toStacks(ctx, {
|
||||||
|
...info,
|
||||||
|
fromChain: route.fromChain as KnownChainId.BRC20Chain,
|
||||||
|
fromToken: route.fromToken as KnownTokenId.BRC20Token,
|
||||||
|
toChain: route.toChain,
|
||||||
|
toToken: route.toToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (KnownChainId.isEVMChain(route.toChain)) {
|
||||||
|
if (
|
||||||
|
isMetaProtocolToken(route.fromToken) &&
|
||||||
|
KnownTokenId.isEVMToken(route.toToken)
|
||||||
|
) {
|
||||||
|
return bridgeInfoFromMeta_toEVM(ctx, {
|
||||||
|
...info,
|
||||||
|
fromChain: route.fromChain as KnownChainId.BRC20Chain,
|
||||||
|
fromToken: route.fromToken as KnownTokenId.BRC20Token,
|
||||||
|
toChain: route.toChain,
|
||||||
|
toToken: route.toToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (KnownChainId.isBitcoinChain(route.toChain)) {
|
||||||
|
if (
|
||||||
|
isMetaProtocolToken(route.fromToken) &&
|
||||||
|
KnownTokenId.isBitcoinToken(route.toToken)
|
||||||
|
) {
|
||||||
|
return bridgeInfoFromMeta_toBitcoin(ctx, {
|
||||||
|
...info,
|
||||||
|
fromChain: route.fromChain as KnownChainId.BRC20Chain,
|
||||||
|
fromToken: route.fromToken as KnownTokenId.BRC20Token,
|
||||||
|
toChain: route.toChain,
|
||||||
|
toToken: route.toToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (KnownChainId.isBRC20Chain(route.toChain)) {
|
||||||
|
if (
|
||||||
|
isMetaProtocolToken(route.fromToken) &&
|
||||||
|
KnownTokenId.isBRC20Token(route.toToken)
|
||||||
|
) {
|
||||||
|
return bridgeInfoFromMeta_toMeta(ctx, {
|
||||||
|
...info,
|
||||||
|
fromChain: route.fromChain as KnownChainId.BRC20Chain,
|
||||||
|
fromToken: route.fromToken as KnownTokenId.BRC20Token,
|
||||||
|
toChain: route.toChain,
|
||||||
|
toToken: route.toToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (KnownChainId.isRunesChain(route.toChain)) {
|
||||||
|
if (
|
||||||
|
isMetaProtocolToken(route.fromToken) &&
|
||||||
|
KnownTokenId.isRunesToken(route.toToken)
|
||||||
|
) {
|
||||||
|
return bridgeInfoFromMeta_toMeta(ctx, {
|
||||||
|
...info,
|
||||||
|
fromChain: route.fromChain as KnownChainId.BRC20Chain,
|
||||||
|
fromToken: route.fromToken as KnownTokenId.BRC20Token,
|
||||||
|
toChain: route.toChain,
|
||||||
|
toToken: route.toToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
checkNever(route.toChain)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assertExclude(route.fromChain, assertExclude.i<KnownChainId.StacksChain>())
|
||||||
|
assertExclude(route.fromChain, assertExclude.i<KnownChainId.EVMChain>())
|
||||||
|
assertExclude(route.fromChain, assertExclude.i<KnownChainId.BitcoinChain>())
|
||||||
|
checkNever(route)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
info.fromToken,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bridgeInfoFromMeta_toStacks(
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
|
info: Omit<
|
||||||
|
BridgeInfoFromMetaInput,
|
||||||
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
|
> &
|
||||||
|
(KnownRoute_FromBRC20_ToStacks | KnownRoute_FromRunes_ToStacks),
|
||||||
|
): Promise<BridgeInfoFromMetaOutput> {
|
||||||
|
const step1 = await getMeta2StacksFeeInfo(ctx, info, {
|
||||||
|
swapRoute: info.swapRoute ?? null,
|
||||||
|
})
|
||||||
|
if (step1 == null) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
info.fromToken,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...transformToPublicTransferProphet(info, info.amount, step1),
|
||||||
|
transferProphets: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bridgeInfoFromMeta_toEVM(
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
|
info: Omit<
|
||||||
|
BridgeInfoFromMetaInput,
|
||||||
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
|
> &
|
||||||
|
(KnownRoute_FromBRC20_ToEVM | KnownRoute_FromRunes_ToEVM),
|
||||||
|
): Promise<BridgeInfoFromMetaOutput> {
|
||||||
|
const transitStacksChainId =
|
||||||
|
info.fromChain === KnownChainId.BRC20.Mainnet ||
|
||||||
|
info.fromChain === KnownChainId.Runes.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet
|
||||||
|
|
||||||
|
const { step1ToStacksToken, step2FromStacksToken } =
|
||||||
|
await getTransitStacksChainTransitStepInfos(ctx, info)
|
||||||
|
|
||||||
|
const step1Route = {
|
||||||
|
fromChain: info.fromChain as KnownChainId.BRC20Chain,
|
||||||
|
fromToken: info.fromToken as KnownTokenId.BRC20Token,
|
||||||
|
toChain: transitStacksChainId,
|
||||||
|
toToken: step1ToStacksToken,
|
||||||
|
} satisfies KnownRoute_WithMetaProtocol
|
||||||
|
const step2Route: KnownRoute = {
|
||||||
|
fromChain: transitStacksChainId,
|
||||||
|
fromToken: step2FromStacksToken,
|
||||||
|
toChain: info.toChain,
|
||||||
|
toToken: info.toToken,
|
||||||
|
} satisfies KnownRoute_WithMetaProtocol
|
||||||
|
|
||||||
|
// TODO: add support for Meta -> EVM with swap
|
||||||
|
if (info.swapRoute != null) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
info.fromToken,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const [step1, step2] = await Promise.all([
|
||||||
|
getMeta2StacksFeeInfo(ctx, step1Route, {
|
||||||
|
swapRoute: info.swapRoute ?? null,
|
||||||
|
}),
|
||||||
|
getStacks2EvmFeeInfo(ctx, step2Route),
|
||||||
|
])
|
||||||
|
if (step1 == null || step2 == null) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
info.fromToken,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformToPublicTransferProphetAggregated2(
|
||||||
|
[step1Route, step2Route],
|
||||||
|
[step1, step2],
|
||||||
|
BigNumber.from(info.amount),
|
||||||
|
BigNumber.ONE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bridgeInfoFromMeta_toBitcoin(
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
|
info: Omit<
|
||||||
|
BridgeInfoFromMetaInput,
|
||||||
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
|
> &
|
||||||
|
(KnownRoute_FromBRC20_ToBitcoin | KnownRoute_FromRunes_ToBitcoin),
|
||||||
|
): Promise<BridgeInfoFromMetaOutput> {
|
||||||
|
const transitStacksChainId =
|
||||||
|
info.fromChain === KnownChainId.BRC20.Mainnet ||
|
||||||
|
info.fromChain === KnownChainId.Runes.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet
|
||||||
|
|
||||||
|
const { step1ToStacksToken, step2FromStacksToken } =
|
||||||
|
await getTransitStacksChainTransitStepInfos(ctx, info)
|
||||||
|
|
||||||
|
const step1Route = {
|
||||||
|
fromChain: info.fromChain as KnownChainId.BRC20Chain,
|
||||||
|
fromToken: info.fromToken as KnownTokenId.BRC20Token,
|
||||||
|
toChain: transitStacksChainId,
|
||||||
|
toToken: step1ToStacksToken,
|
||||||
|
} satisfies KnownRoute_WithMetaProtocol
|
||||||
|
const step2Route: KnownRoute = {
|
||||||
|
fromChain: transitStacksChainId,
|
||||||
|
fromToken: step2FromStacksToken,
|
||||||
|
toChain: info.toChain,
|
||||||
|
toToken: info.toToken,
|
||||||
|
} satisfies KnownRoute_WithMetaProtocol
|
||||||
|
|
||||||
|
const [step1, step2] = await Promise.all([
|
||||||
|
getMeta2StacksFeeInfo(ctx, step1Route, {
|
||||||
|
swapRoute: info.swapRoute ?? null,
|
||||||
|
}),
|
||||||
|
getStacks2BtcFeeInfo(step2Route, {
|
||||||
|
swappedFromRoute: step1Route,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
if (step1 == null || step2 == null) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
info.fromToken,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformToPublicTransferProphetAggregated2(
|
||||||
|
[step1Route, step2Route],
|
||||||
|
[step1, step2],
|
||||||
|
BigNumber.from(info.amount),
|
||||||
|
BigNumber.from(info.swapRoute?.composedExchangeRate ?? BigNumber.ONE),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bridgeInfoFromMeta_toMeta(
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
|
info: Omit<
|
||||||
|
BridgeInfoFromMetaInput,
|
||||||
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
|
> &
|
||||||
|
(
|
||||||
|
| KnownRoute_FromBRC20_ToBRC20
|
||||||
|
| KnownRoute_FromBRC20_ToRunes
|
||||||
|
| KnownRoute_FromRunes_ToBRC20
|
||||||
|
| KnownRoute_FromRunes_ToRunes
|
||||||
|
),
|
||||||
|
): Promise<BridgeInfoFromMetaOutput> {
|
||||||
|
const transitStacksChain =
|
||||||
|
info.fromChain === KnownChainId.BRC20.Mainnet ||
|
||||||
|
info.fromChain === KnownChainId.Runes.Mainnet
|
||||||
|
? KnownChainId.Stacks.Mainnet
|
||||||
|
: KnownChainId.Stacks.Testnet
|
||||||
|
|
||||||
|
const { step1ToStacksToken, step2FromStacksToken } =
|
||||||
|
await getTransitStacksChainTransitStepInfos(ctx, info)
|
||||||
|
|
||||||
|
const step1Route:
|
||||||
|
| KnownRoute_FromBRC20_ToStacks
|
||||||
|
| KnownRoute_FromRunes_ToStacks = {
|
||||||
|
fromChain: info.fromChain as any,
|
||||||
|
fromToken: info.fromToken as any,
|
||||||
|
toChain: transitStacksChain,
|
||||||
|
toToken: step1ToStacksToken,
|
||||||
|
} satisfies KnownRoute_WithMetaProtocol
|
||||||
|
const step2Route:
|
||||||
|
| KnownRoute_FromStacks_ToBRC20
|
||||||
|
| KnownRoute_FromStacks_ToRunes = {
|
||||||
|
fromChain: transitStacksChain,
|
||||||
|
fromToken: step2FromStacksToken,
|
||||||
|
toChain: info.toChain as any,
|
||||||
|
toToken: info.toToken as any,
|
||||||
|
}
|
||||||
|
|
||||||
|
const [step1, step2] = await Promise.all([
|
||||||
|
getMeta2StacksFeeInfo(ctx, step1Route, {
|
||||||
|
swapRoute: info.swapRoute ?? null,
|
||||||
|
}),
|
||||||
|
getStacks2MetaFeeInfo(ctx, step2Route),
|
||||||
|
])
|
||||||
|
if (step1 == null || step2 == null) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
info.fromToken,
|
||||||
|
info.toToken,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformToPublicTransferProphetAggregated2(
|
||||||
|
[step1Route, step2Route],
|
||||||
|
[step1, step2],
|
||||||
|
BigNumber.from(info.amount),
|
||||||
|
BigNumber.from(info.swapRoute?.composedExchangeRate ?? BigNumber.ONE),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -54,7 +54,7 @@ export async function bridgeInfoFromStacks(
|
|||||||
KnownTokenId.isStacksToken(route.fromToken) &&
|
KnownTokenId.isStacksToken(route.fromToken) &&
|
||||||
KnownTokenId.isEVMToken(route.toToken)
|
KnownTokenId.isEVMToken(route.toToken)
|
||||||
) {
|
) {
|
||||||
return bridgeInfoFromStacks_toEVM({
|
return bridgeInfoFromStacks_toEVM(ctx, {
|
||||||
...info,
|
...info,
|
||||||
fromChain: route.fromChain,
|
fromChain: route.fromChain,
|
||||||
toChain: route.toChain,
|
toChain: route.toChain,
|
||||||
@@ -113,7 +113,9 @@ async function bridgeInfoFromStacks_toBitcoin(
|
|||||||
> &
|
> &
|
||||||
KnownRoute_FromStacks_ToBitcoin,
|
KnownRoute_FromStacks_ToBitcoin,
|
||||||
): Promise<BridgeInfoFromStacksOutput> {
|
): Promise<BridgeInfoFromStacksOutput> {
|
||||||
const step1 = await getStacks2BtcFeeInfo(info)
|
const step1 = await getStacks2BtcFeeInfo(info, {
|
||||||
|
swappedFromRoute: null,
|
||||||
|
})
|
||||||
if (step1 == null) {
|
if (step1 == null) {
|
||||||
throw new UnsupportedBridgeRouteError(
|
throw new UnsupportedBridgeRouteError(
|
||||||
info.fromChain,
|
info.fromChain,
|
||||||
@@ -130,13 +132,14 @@ async function bridgeInfoFromStacks_toBitcoin(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function bridgeInfoFromStacks_toEVM(
|
async function bridgeInfoFromStacks_toEVM(
|
||||||
|
ctx: SDKGlobalContext,
|
||||||
info: Omit<
|
info: Omit<
|
||||||
BridgeInfoFromStacksInput,
|
BridgeInfoFromStacksInput,
|
||||||
"fromChain" | "toChain" | "fromToken" | "toToken"
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
> &
|
> &
|
||||||
KnownRoute_FromStacks_ToEVM,
|
KnownRoute_FromStacks_ToEVM,
|
||||||
): Promise<BridgeInfoFromStacksOutput> {
|
): Promise<BridgeInfoFromStacksOutput> {
|
||||||
const step1 = await getStacks2EvmFeeInfo(info)
|
const step1 = await getStacks2EvmFeeInfo(ctx, info)
|
||||||
if (step1 == null) {
|
if (step1 == null) {
|
||||||
throw new UnsupportedBridgeRouteError(
|
throw new UnsupportedBridgeRouteError(
|
||||||
info.fromChain,
|
info.fromChain,
|
||||||
|
|||||||
@@ -2,14 +2,29 @@ import { getBTCPegInAddress } from "../bitcoinUtils/btcAddresses"
|
|||||||
import { ReselectSpendableUTXOsFn } from "../bitcoinUtils/prepareTransaction"
|
import { ReselectSpendableUTXOsFn } from "../bitcoinUtils/prepareTransaction"
|
||||||
import {
|
import {
|
||||||
createBridgeOrder_BitcoinToEVM,
|
createBridgeOrder_BitcoinToEVM,
|
||||||
|
createBridgeOrder_BitcoinToMeta,
|
||||||
createBridgeOrder_BitcoinToStacks,
|
createBridgeOrder_BitcoinToStacks,
|
||||||
} from "../stacksUtils/createBridgeOrder"
|
} from "../stacksUtils/createBridgeOrderFromBitcoin"
|
||||||
import { numberToStacksContractNumber } from "../stacksUtils/xlinkContractHelpers"
|
import { BigNumber } from "../utils/BigNumber"
|
||||||
import { UnsupportedBridgeRouteError } from "../utils/errors"
|
import {
|
||||||
|
KnownRoute_FromBitcoin_ToBRC20,
|
||||||
|
KnownRoute_FromBitcoin_ToEVM,
|
||||||
|
KnownRoute_FromBitcoin_ToRunes,
|
||||||
|
KnownRoute_FromBitcoin_ToStacks,
|
||||||
|
} from "../utils/buildSupportedRoutes"
|
||||||
|
import {
|
||||||
|
InvalidMethodParametersError,
|
||||||
|
UnsupportedBridgeRouteError,
|
||||||
|
} from "../utils/errors"
|
||||||
import { assertExclude, checkNever } from "../utils/typeHelpers"
|
import { assertExclude, checkNever } from "../utils/typeHelpers"
|
||||||
import { KnownChainId, KnownTokenId } from "../utils/types/knownIds"
|
import {
|
||||||
|
_knownChainIdToErrorMessagePart,
|
||||||
|
KnownChainId,
|
||||||
|
KnownTokenId,
|
||||||
|
} from "../utils/types/knownIds"
|
||||||
import { prepareBitcoinTransaction, supportedRoutes } from "./bridgeFromBitcoin"
|
import { prepareBitcoinTransaction, supportedRoutes } from "./bridgeFromBitcoin"
|
||||||
import { ChainId, SDKNumber, TokenId, toSDKNumberOrUndefined } from "./types"
|
import { ChainId, SDKNumber, TokenId, toSDKNumberOrUndefined } from "./types"
|
||||||
|
import { SwapRoute_WithMinimumAmountsToReceive_Public } from "../utils/SwapRouteHelpers"
|
||||||
import { SDKGlobalContext } from "./types.internal"
|
import { SDKGlobalContext } from "./types.internal"
|
||||||
|
|
||||||
export interface EstimateBridgeTransactionFromBitcoinInput {
|
export interface EstimateBridgeTransactionFromBitcoinInput {
|
||||||
@@ -20,7 +35,12 @@ export interface EstimateBridgeTransactionFromBitcoinInput {
|
|||||||
fromAddress: string
|
fromAddress: string
|
||||||
fromAddressScriptPubKey: Uint8Array
|
fromAddressScriptPubKey: Uint8Array
|
||||||
toAddress: string
|
toAddress: string
|
||||||
|
/**
|
||||||
|
* **Required** when `toChain` is one of bitcoin chains
|
||||||
|
*/
|
||||||
|
toAddressScriptPubKey?: Uint8Array
|
||||||
amount: SDKNumber
|
amount: SDKNumber
|
||||||
|
swapRoute?: SwapRoute_WithMinimumAmountsToReceive_Public
|
||||||
networkFeeRate: bigint
|
networkFeeRate: bigint
|
||||||
reselectSpendableUTXOs: ReselectSpendableUTXOsFn
|
reselectSpendableUTXOs: ReselectSpendableUTXOsFn
|
||||||
}
|
}
|
||||||
@@ -63,13 +83,32 @@ export async function estimateBridgeTransactionFromBitcoin(
|
|||||||
toToken: route.toToken,
|
toToken: route.toToken,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (KnownChainId.isBRC20Chain(route.toChain)) {
|
||||||
KnownChainId.isBRC20Chain(route.toChain) ||
|
if (
|
||||||
KnownChainId.isRunesChain(route.toChain)
|
KnownTokenId.isBitcoinToken(route.fromToken) &&
|
||||||
) {
|
KnownTokenId.isBRC20Token(route.toToken)
|
||||||
assertExclude(route.toChain, assertExclude.i<KnownChainId.BRC20Chain>())
|
) {
|
||||||
assertExclude(route.toChain, assertExclude.i<KnownChainId.RunesChain>())
|
return estimateFromBitcoin_toMeta(ctx, {
|
||||||
// TODO: bitcoin to brc20/runes is not supported yet
|
...info,
|
||||||
|
fromChain: route.fromChain,
|
||||||
|
toChain: route.toChain,
|
||||||
|
fromToken: route.fromToken,
|
||||||
|
toToken: route.toToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (KnownChainId.isRunesChain(route.toChain)) {
|
||||||
|
if (
|
||||||
|
KnownTokenId.isBitcoinToken(route.fromToken) &&
|
||||||
|
KnownTokenId.isRunesToken(route.toToken)
|
||||||
|
) {
|
||||||
|
return estimateFromBitcoin_toMeta(ctx, {
|
||||||
|
...info,
|
||||||
|
fromChain: route.fromChain,
|
||||||
|
toChain: route.toChain,
|
||||||
|
fromToken: route.fromToken,
|
||||||
|
toToken: route.toToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
assertExclude(route.toChain, assertExclude.i<KnownChainId.BitcoinChain>())
|
assertExclude(route.toChain, assertExclude.i<KnownChainId.BitcoinChain>())
|
||||||
checkNever(route)
|
checkNever(route)
|
||||||
@@ -89,16 +128,12 @@ export async function estimateBridgeTransactionFromBitcoin(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function estimateFromBitcoin_toStacks(
|
async function estimateFromBitcoin_toStacks(
|
||||||
sdkContext: Pick<SDKGlobalContext, "backendAPI">,
|
sdkContext: SDKGlobalContext,
|
||||||
info: Omit<
|
info: Omit<
|
||||||
EstimateBridgeTransactionFromBitcoinInput,
|
EstimateBridgeTransactionFromBitcoinInput,
|
||||||
"fromChain" | "toChain" | "fromToken" | "toToken"
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
> & {
|
> &
|
||||||
fromChain: KnownChainId.BitcoinChain
|
KnownRoute_FromBitcoin_ToStacks,
|
||||||
toChain: KnownChainId.StacksChain
|
|
||||||
fromToken: KnownTokenId.BitcoinToken
|
|
||||||
toToken: KnownTokenId.StacksToken
|
|
||||||
},
|
|
||||||
): Promise<EstimateBridgeTransactionFromBitcoinOutput> {
|
): Promise<EstimateBridgeTransactionFromBitcoinOutput> {
|
||||||
const pegInAddress = getBTCPegInAddress(info.fromChain, info.toChain)
|
const pegInAddress = getBTCPegInAddress(info.fromChain, info.toChain)
|
||||||
if (pegInAddress == null) {
|
if (pegInAddress == null) {
|
||||||
@@ -109,14 +144,21 @@ async function estimateFromBitcoin_toStacks(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdOrder = await createBridgeOrder_BitcoinToStacks({
|
const createdOrder = await createBridgeOrder_BitcoinToStacks(sdkContext, {
|
||||||
fromChain: info.fromChain,
|
fromChain: info.fromChain,
|
||||||
fromBitcoinScriptPubKey: info.fromAddressScriptPubKey,
|
fromBitcoinScriptPubKey: info.fromAddressScriptPubKey,
|
||||||
toChain: info.toChain,
|
toChain: info.toChain,
|
||||||
toToken: info.toToken,
|
toToken: info.toToken,
|
||||||
toStacksAddress: info.toAddress,
|
toStacksAddress: info.toAddress,
|
||||||
swapSlippedAmount: numberToStacksContractNumber(info.amount),
|
swap:
|
||||||
swapRoute: [],
|
info.swapRoute == null
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
...info.swapRoute,
|
||||||
|
minimumAmountsToReceive: BigNumber.from(
|
||||||
|
info.swapRoute.minimumAmountsToReceive,
|
||||||
|
),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if (createdOrder == null) {
|
if (createdOrder == null) {
|
||||||
throw new UnsupportedBridgeRouteError(
|
throw new UnsupportedBridgeRouteError(
|
||||||
@@ -139,16 +181,12 @@ async function estimateFromBitcoin_toStacks(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function estimateFromBitcoin_toEVM(
|
async function estimateFromBitcoin_toEVM(
|
||||||
sdkContext: Pick<SDKGlobalContext, "backendAPI">,
|
sdkContext: SDKGlobalContext,
|
||||||
info: Omit<
|
info: Omit<
|
||||||
EstimateBridgeTransactionFromBitcoinInput,
|
EstimateBridgeTransactionFromBitcoinInput,
|
||||||
"fromChain" | "toChain" | "fromToken" | "toToken"
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
> & {
|
> &
|
||||||
fromChain: KnownChainId.BitcoinChain
|
KnownRoute_FromBitcoin_ToEVM,
|
||||||
toChain: KnownChainId.EVMChain
|
|
||||||
fromToken: KnownTokenId.BitcoinToken
|
|
||||||
toToken: KnownTokenId.EVMToken
|
|
||||||
},
|
|
||||||
): Promise<EstimateBridgeTransactionFromBitcoinOutput> {
|
): Promise<EstimateBridgeTransactionFromBitcoinOutput> {
|
||||||
const pegInAddress = getBTCPegInAddress(info.fromChain, info.toChain)
|
const pegInAddress = getBTCPegInAddress(info.fromChain, info.toChain)
|
||||||
if (pegInAddress == null) {
|
if (pegInAddress == null) {
|
||||||
@@ -159,14 +197,88 @@ async function estimateFromBitcoin_toEVM(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdOrder = await createBridgeOrder_BitcoinToEVM({
|
const createdOrder = await createBridgeOrder_BitcoinToEVM(sdkContext, {
|
||||||
fromChain: info.fromChain,
|
fromChain: info.fromChain,
|
||||||
toChain: info.toChain,
|
toChain: info.toChain,
|
||||||
toToken: info.toToken,
|
toToken: info.toToken,
|
||||||
fromBitcoinScriptPubKey: info.fromAddressScriptPubKey,
|
fromBitcoinScriptPubKey: info.fromAddressScriptPubKey,
|
||||||
toEVMAddress: info.toAddress,
|
toEVMAddress: info.toAddress,
|
||||||
swapSlippedAmount: numberToStacksContractNumber(info.amount),
|
swap:
|
||||||
swapRoute: [],
|
info.swapRoute == null
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
...info.swapRoute,
|
||||||
|
minimumAmountsToReceive: BigNumber.from(
|
||||||
|
info.swapRoute.minimumAmountsToReceive,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (createdOrder == null) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
KnownTokenId.Bitcoin.BTC,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const resp = await prepareBitcoinTransaction(sdkContext, {
|
||||||
|
...info,
|
||||||
|
orderData: createdOrder.data,
|
||||||
|
pegInAddress,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
fee: toSDKNumberOrUndefined(resp.fee),
|
||||||
|
estimatedVSize: toSDKNumberOrUndefined(resp.estimatedVSize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function estimateFromBitcoin_toMeta(
|
||||||
|
sdkContext: SDKGlobalContext,
|
||||||
|
info: Omit<
|
||||||
|
EstimateBridgeTransactionFromBitcoinInput,
|
||||||
|
"fromChain" | "toChain" | "fromToken" | "toToken"
|
||||||
|
> &
|
||||||
|
(KnownRoute_FromBitcoin_ToBRC20 | KnownRoute_FromBitcoin_ToRunes),
|
||||||
|
): Promise<EstimateBridgeTransactionFromBitcoinOutput> {
|
||||||
|
if (info.toAddressScriptPubKey == null) {
|
||||||
|
throw new InvalidMethodParametersError(
|
||||||
|
[
|
||||||
|
"XLinkSDK",
|
||||||
|
`estimateBridgeTransactionFromBitcoin (to ${_knownChainIdToErrorMessagePart(info.toChain)})`,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: "toAddressScriptPubKey",
|
||||||
|
expected: "Uint8Array",
|
||||||
|
received: "undefined",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const pegInAddress = getBTCPegInAddress(info.fromChain, info.toChain)
|
||||||
|
if (pegInAddress == null) {
|
||||||
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
info.fromChain,
|
||||||
|
info.toChain,
|
||||||
|
KnownTokenId.Bitcoin.BTC,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdOrder = await createBridgeOrder_BitcoinToMeta(sdkContext, {
|
||||||
|
...info,
|
||||||
|
fromBitcoinScriptPubKey: info.fromAddressScriptPubKey,
|
||||||
|
toBitcoinScriptPubKey: info.toAddressScriptPubKey,
|
||||||
|
swap:
|
||||||
|
info.swapRoute == null
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
...info.swapRoute,
|
||||||
|
minimumAmountsToReceive: BigNumber.from(
|
||||||
|
info.swapRoute.minimumAmountsToReceive,
|
||||||
|
),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if (createdOrder == null) {
|
if (createdOrder == null) {
|
||||||
throw new UnsupportedBridgeRouteError(
|
throw new UnsupportedBridgeRouteError(
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Client } from "viem"
|
import { Client } from "viem"
|
||||||
import { EVMOnChainAddresses } from "../evmUtils/evmContractAddresses"
|
import { EVMOnChainAddresses } from "../evmUtils/evmContractAddresses"
|
||||||
import { KnownChainId } from "../utils/types/knownIds"
|
|
||||||
import type {
|
import type {
|
||||||
BRC20SupportedRoute,
|
BRC20SupportedRoute,
|
||||||
RunesSupportedRoute,
|
RunesSupportedRoute,
|
||||||
} from "../metaUtils/xlinkContractHelpers"
|
} from "../metaUtils/xlinkContractHelpers"
|
||||||
|
import { StacksTokenInfo } from "../stacksUtils/xlinkContractHelpers"
|
||||||
|
import { KnownChainId } from "../utils/types/knownIds"
|
||||||
|
import { EVMAddress } from "./types"
|
||||||
|
|
||||||
export interface SDKGlobalContextCache<K, T> {
|
export interface SDKGlobalContextCache<K, T> {
|
||||||
get: (key: K) => T | null
|
get: (key: K) => T | null
|
||||||
@@ -16,22 +18,33 @@ export interface SDKGlobalContext {
|
|||||||
backendAPI: {
|
backendAPI: {
|
||||||
runtimeEnv: "prod" | "dev"
|
runtimeEnv: "prod" | "dev"
|
||||||
}
|
}
|
||||||
|
stacks: {
|
||||||
|
tokensCache?: SDKGlobalContextCache<
|
||||||
|
KnownChainId.StacksChain,
|
||||||
|
Promise<StacksTokenInfo[]>
|
||||||
|
>
|
||||||
|
}
|
||||||
|
btc: {
|
||||||
|
ignoreValidateResult?: boolean
|
||||||
|
}
|
||||||
brc20: {
|
brc20: {
|
||||||
routesConfigCache?: SDKGlobalContextCache<
|
routesConfigCache?: SDKGlobalContextCache<
|
||||||
string,
|
KnownChainId.BRC20Chain,
|
||||||
Promise<BRC20SupportedRoute[]>
|
Promise<BRC20SupportedRoute[]>
|
||||||
>
|
>
|
||||||
|
ignoreValidateResult?: boolean
|
||||||
}
|
}
|
||||||
runes: {
|
runes: {
|
||||||
routesConfigCache?: SDKGlobalContextCache<
|
routesConfigCache?: SDKGlobalContextCache<
|
||||||
string,
|
KnownChainId.RunesChain,
|
||||||
Promise<RunesSupportedRoute[]>
|
Promise<RunesSupportedRoute[]>
|
||||||
>
|
>
|
||||||
|
ignoreValidateResult?: boolean
|
||||||
}
|
}
|
||||||
evm: {
|
evm: {
|
||||||
enableMulticall?: boolean
|
enableMulticall?: boolean
|
||||||
onChainConfigCache?: SDKGlobalContextCache<
|
onChainConfigCache?: SDKGlobalContextCache<
|
||||||
string,
|
`${KnownChainId.EVMChain}:${EVMAddress}`,
|
||||||
Promise<EVMOnChainAddresses>
|
Promise<EVMOnChainAddresses>
|
||||||
>
|
>
|
||||||
viemClients: Partial<Record<KnownChainId.EVMChain, Client>>
|
viemClients: Partial<Record<KnownChainId.EVMChain, Client>>
|
||||||
|
|||||||
12
tsup.config.ts
Normal file
12
tsup.config.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { defineConfig } from "tsup"
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
outDir: "lib",
|
||||||
|
format: ["cjs", "esm"],
|
||||||
|
treeshake: true,
|
||||||
|
sourcemap: true,
|
||||||
|
dts: true,
|
||||||
|
env: {
|
||||||
|
ENV_NAME: process.env.ENV_NAME ?? "prod",
|
||||||
|
},
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user