Skip to content

Commit

Permalink
refactor: optimize routes logic
Browse files Browse the repository at this point in the history
  • Loading branch information
bolasblack committed Jul 23, 2024
1 parent 2afcf21 commit 5274802
Show file tree
Hide file tree
Showing 22 changed files with 575 additions and 737 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"test": "vitest --exclude lib"
},
"dependencies": {
"@c4/btc-utils": "^0.2.0",
"@c4/btc-utils": "^0.3.1",
"clarity-codegen": "^0.5.2"
},
"devDependencies": {
Expand Down
16 changes: 8 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions scripts/printAllSupportedRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { XLinkSDK } from "../src"
import { KnownRoute } from "../src/utils/buildSupportedRoutes"

async function main(): Promise<void> {
const sdk = new XLinkSDK()
const supportedRoutes = await sdk.getSupportedRoutes()

const group: Record<string, KnownRoute[]> = {}
for (const route of supportedRoutes) {
const key = `${route.fromChain} -> ${route.toChain}`
if (!group[key]) group[key] = []
group[key].push(route)
}

const groupEntries = Object.entries(group)
for (const [group, routes] of groupEntries) {
console.log(group)
for (const route of routes) {
console.log(` ${route.fromToken} -> ${route.toToken}`)
}
}
}

main().catch(console.error)
19 changes: 3 additions & 16 deletions src/XLinkSDK.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ import {
ChainId,
EVMAddress,
StacksContractAddress,
SupportedToken,
} from "./xlinkSdkUtils/types"
import { GetSupportedRoutesFn } from "./utils/buildSupportedRoutes"

export {
BridgeFromBitcoinInput,
Expand Down Expand Up @@ -67,25 +67,12 @@ export {
} from "./xlinkSdkUtils/timelockFromEVM"

export class XLinkSDK {
async getSupportedTokens(
fromChain: ChainId,
toChain: ChainId,
): Promise<SupportedToken[]> {
getSupportedRoutes: GetSupportedRoutesFn = async conditions => {
const promises = [
supportedRoutesFromStacks,
supportedRoutesFromEVM,
supportedRoutesFromBitcoin,
].map(async rules => {
const result = await rules.getSupportedTokens(fromChain, toChain)

return result.map(res => ({
fromChain: res.fromChain,
fromToken: res.fromToken,
toChain: res.toChain,
toToken: res.toToken,
}))
})

].map(async rules => rules.getSupportedRoutes(conditions))
return (await Promise.all(promises)).flat()
}

Expand Down
10 changes: 6 additions & 4 deletions src/bitcoinUtils/errors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export class InsufficientBalanceError extends Error {
import { XLinkSDKErrorBase } from "../utils/errors"

export class InsufficientBitcoinBalanceError extends XLinkSDKErrorBase {
constructor(...args: ConstructorParameters<typeof Error>) {
super(...args)

Expand All @@ -7,10 +9,10 @@ export class InsufficientBalanceError extends Error {
}
}

export class UnsupportedBitcoinInput extends Error {
export class UnsupportedBitcoinInput extends XLinkSDKErrorBase {
constructor(
txid: string,
index: number,
public txid: string,
public index: number,
...args: ConstructorParameters<typeof Error>
) {
super(...args)
Expand Down
51 changes: 50 additions & 1 deletion src/bitcoinUtils/peggingHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { StacksNetwork } from "@stacks/network"
import { callReadOnlyFunction } from "@stacks/transactions"
import { CallReadOnlyFunctionFn } from "clarity-codegen"
import { fromCorrespondingStacksCurrency } from "../evmUtils/peggingHelpers"
import { getEVMTokenContractInfo } from "../evmUtils/xlinkContractHelpers"
import { stxTokenContractAddresses } from "../stacksUtils/stxContractAddresses"
import {
executeReadonlyCallXLINK,
numberFromStacksContractNumber,
} from "../stacksUtils/xlinkContractHelpers"
import { BigNumber } from "../utils/BigNumber"
import { props } from "../utils/promiseHelpers"
import { IsSupportedFn } from "../utils/buildSupportedRoutes"
import { TransferProphet } from "../utils/feeRateHelpers"
import { props } from "../utils/promiseHelpers"
import { checkNever } from "../utils/typeHelpers"
import { KnownChainId, KnownTokenId } from "../utils/types.internal"

export const getBtc2StacksFeeInfo = async (contractCallInfo: {
network: StacksNetwork
Expand Down Expand Up @@ -94,3 +100,46 @@ export const getStacks2BtcFeeInfo = async (contractCallInfo: {
maxAmount: null,
}
}

export const isSupportedBitcoinRoute: IsSupportedFn = async route => {
if (route.fromChain === route.toChain && route.fromToken === route.toToken) {
return false
}
if (
!KnownChainId.isBitcoinChain(route.fromChain) ||
!KnownTokenId.isBitcoinToken(route.fromToken)
) {
return false
}
if (!KnownChainId.isKnownChain(route.toChain)) return false

if (KnownChainId.isBitcoinChain(route.toChain)) {
return false
}

if (KnownChainId.isStacksChain(route.toChain)) {
if (!KnownTokenId.isStacksToken(route.toToken)) return false

return (
route.fromToken === KnownTokenId.Bitcoin.BTC &&
route.toToken === KnownTokenId.Stacks.aBTC &&
stxTokenContractAddresses[route.toToken]?.[route.toChain] != null
)
}

if (KnownChainId.isEVMChain(route.toChain)) {
const toEVMToken = await fromCorrespondingStacksCurrency(
route.toChain,
KnownTokenId.Stacks.aBTC,
)
if (toEVMToken == null) return false

const info = await getEVMTokenContractInfo(route.toChain, toEVMToken)
if (info == null) return false

return toEVMToken === route.toToken
}

checkNever(route.toChain)
return false
}
9 changes: 6 additions & 3 deletions src/bitcoinUtils/prepareTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import {
UnsupportedInputTypeError,
} from "@c4/btc-utils"
import * as btc from "@scure/btc-signer"
import { InsufficientBalanceError, UnsupportedBitcoinInput } from "./errors"
import {
InsufficientBitcoinBalanceError,
UnsupportedBitcoinInput,
} from "./errors"
import { max, sum } from "../utils/bigintHelpers"
import {
addressToScriptPubKey,
Expand Down Expand Up @@ -93,7 +96,7 @@ export async function prepareTransaction(txInfo: {
// Check if selected UTXO satoshi amount has changed since last iteration
// If it hasn't, there is insufficient balance
if (newSelectedUTXOSatsInTotal < lastSelectedUTXOSatsInTotal) {
throw new InsufficientBalanceError()
throw new InsufficientBitcoinBalanceError()
}

lastSelectedUTXOSatsInTotal = newSelectedUTXOSatsInTotal
Expand All @@ -111,7 +114,7 @@ export async function prepareTransaction(txInfo: {
loopTimes++
if (loopTimes > 500) {
// Exit after max 500 iterations
throw new InsufficientBalanceError()
throw new InsufficientBitcoinBalanceError()
}
}

Expand Down
76 changes: 54 additions & 22 deletions src/evmUtils/peggingHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ import {
numberFromStacksContractNumber,
} from "../stacksUtils/xlinkContractHelpers"
import { BigNumber } from "../utils/BigNumber"
import { IsSupportedFn } from "../utils/buildSupportedRoutes"
import { TransferProphet } from "../utils/feeRateHelpers"
import { props } from "../utils/promiseHelpers"
import { assertExclude, checkNever } from "../utils/typeHelpers"
import { KnownChainId, KnownTokenId } from "../utils/types.internal"
import { StacksContractAddress } from "../xlinkSdkUtils/types"
import { bridgeEndpointAbi } from "./contractAbi/bridgeEndpoint"
import { bridgeRegistryAbi } from "./contractAbi/bridgeRegistry"
import { numberFromSolidityContractNumber } from "./xlinkContractHelpers"
import { TransferProphet } from "../utils/feeRateHelpers"
import { ChainToken, StacksContractAddress } from "../xlinkSdkUtils/types"
import { KnownChainId, KnownTokenId } from "../utils/types.internal"
import { assertExclude, checkNever } from "../utils/typeHelpers"
import {
getEVMTokenContractInfo,
numberFromSolidityContractNumber,
} from "./xlinkContractHelpers"
import { stxTokenContractAddresses } from "../stacksUtils/stxContractAddresses"

export const getEvm2StacksFeeInfo = async (
stacksContractCallInfo: {
Expand Down Expand Up @@ -304,31 +309,58 @@ export async function toCorrespondingStacksCurrency(
}
}

export async function isValidRoute(
from: ChainToken,
to: ChainToken,
): Promise<boolean> {
if (from.chain === to.chain && from.token === to.token) {
export const isSupportedEVMRoute: IsSupportedFn = async route => {
if (route.fromChain === route.toChain && route.fromToken === route.toToken) {
return false
}

if (
!KnownChainId.isEVMChain(from.chain) ||
!KnownChainId.isEVMChain(to.chain)
!KnownChainId.isEVMChain(route.fromChain) ||
!KnownTokenId.isEVMToken(route.fromToken)
) {
return false
}
if (!KnownChainId.isKnownChain(route.toChain)) return false

const transitStacksToken = await toCorrespondingStacksCurrency(
from.token as KnownTokenId.EVMToken,
const fromTokenInfo = await getEVMTokenContractInfo(
route.fromChain,
route.fromToken,
)
if (transitStacksToken == null) return false
if (fromTokenInfo == null) return false

const toEVMToken = await fromCorrespondingStacksCurrency(
to.chain,
transitStacksToken,
)
if (toEVMToken == null) return false
if (KnownChainId.isStacksChain(route.toChain)) {
const stacksToken = await toCorrespondingStacksCurrency(route.fromToken)
if (stacksToken == null) return false

if (stxTokenContractAddresses[stacksToken]?.[route.toChain] == null) {
return false
}

return stacksToken === route.toToken
}

if (KnownChainId.isEVMChain(route.toChain)) {
const transitStacksToken = await toCorrespondingStacksCurrency(
route.fromToken,
)
if (transitStacksToken == null) return false

const toEVMToken = await fromCorrespondingStacksCurrency(
route.toChain,
transitStacksToken,
)
if (toEVMToken == null) return false

const toTokenInfo = await getEVMTokenContractInfo(route.toChain, toEVMToken)
if (toTokenInfo == null) return false

return toEVMToken === route.toToken
}

if (KnownChainId.isBitcoinChain(route.toChain)) {
const stacksToken = await toCorrespondingStacksCurrency(route.fromToken)
return stacksToken === KnownTokenId.Stacks.aBTC
}

return toEVMToken === to.token
checkNever(route.toChain)
return false
}
Loading

0 comments on commit 5274802

Please sign in to comment.