feat: add tron and solana related paths

This commit is contained in:
Kyle Fang
2025-05-18 03:49:29 +00:00
parent 5d6c069abb
commit f47482141a
6 changed files with 269 additions and 2 deletions

View File

@@ -746,6 +746,9 @@ packages:
base-x@4.0.1:
resolution: {integrity: sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==}
base-x@5.0.1:
resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==}
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@@ -767,6 +770,12 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
bs58@6.0.0:
resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==}
bs58check@4.0.0:
resolution: {integrity: sha512-FsGDOnFg9aVI9erdriULkd/JjEWONV/lQE5aYziB5PoBsXRind56lh8doIZIc9X4HoxT5x4bLjMWN1/NB8Zp5g==}
c32check@2.0.0:
resolution: {integrity: sha512-rpwfAcS/CMqo0oCqDf3r9eeLgScRE3l/xHDCXhM3UyrfvIn7PrLq63uHh7yYbv8NzaZn5MVsVhIRpQ+5GZ5HyA==}
engines: {node: '>=8'}
@@ -1413,6 +1422,7 @@ snapshots:
'@stacks/network': 7.0.2
'@stacks/transactions': 7.0.5
big.js: 6.2.2
bs58check: 4.0.0
c32check: 2.0.0
clarity-codegen: 1.1.3(@stacks/common@7.0.2)(@stacks/transactions@7.0.5)
viem: 2.23.11(typescript@5.8.2)
@@ -1864,6 +1874,8 @@ snapshots:
base-x@4.0.1: {}
base-x@5.0.1: {}
base64-js@1.5.1: {}
big-integer@1.6.52: {}
@@ -1893,6 +1905,15 @@ snapshots:
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.24.4)
bs58@6.0.0:
dependencies:
base-x: 5.0.1
bs58check@4.0.0:
dependencies:
'@noble/hashes': 1.8.0
bs58: 6.0.0
c32check@2.0.0:
dependencies:
'@noble/hashes': 1.7.1

View File

@@ -293,6 +293,9 @@ export class BroSDK {
...options.evm?.viemClients,
},
},
tron: {
routesConfigCache: new Map(),
},
}
}
@@ -1080,9 +1083,9 @@ export class BroSDK {
* - `toChain: ChainId` - The ID of the destination blockchain (Stacks, EVM, Bitcoin or BRC20).
* - `fromToken: TokenId` - The token being transferred from the Runes chain.
* - `toToken: TokenId` - The token expected on the destination chain.
* - `fromAddress: string` - The senders address on the Runes chain.
* - `fromAddress: string` - The sender's address on the Runes chain.
* - `fromAddressScriptPubKey: Uint8Array` - The script public key for `fromAddress`.
* - `toAddress: string` - The recipients address on the destination blockchain.
* - `toAddress: string` - The recipient's address on the destination blockchain.
* - `toAddressScriptPubKey?: Uint8Array` - Required when the destination chain is Bitcoin or BRC20.
* - `amount: SDKNumber` - The amount of tokens to transfer.
* - `inputRuneUTXOs: RunesUTXOSpendable[]` - UTXOs containing the Runes to be spent.

View File

@@ -0,0 +1,116 @@
import { getStacksToken } from "../../stacksUtils/contractHelpers"
import { requestAPI } from "../../utils/apiHelpers"
import { BigNumber } from "../../utils/BigNumber"
import { isNotNull } from "../../utils/typeHelpers"
import { KnownChainId, KnownTokenId } from "../../utils/types/knownIds"
import { StacksContractAddress } from "../../sdkUtils/types"
import { SDKGlobalContext } from "../../sdkUtils/types.internal"
export interface SolanaSupportedRoute {
evmToken: KnownTokenId.EVMToken
stacksChain: KnownChainId.StacksChain
stacksToken: KnownTokenId.StacksToken
proxyStacksTokenContractAddress: null | StacksContractAddress
pegOutFeeRate: BigNumber
pegOutMinFeeAmount: null | BigNumber
pegOutMinAmount: null | BigNumber
pegOutMaxAmount: null | BigNumber
}
type NetworkType = "mainnet" | "testnet"
export async function getSolanaSupportedRoutes(
sdkContext: SDKGlobalContext,
): Promise<SolanaSupportedRoute[]> {
return getSolanaSupportedRoutesByNetwork(sdkContext, "mainnet" as NetworkType)
}
async function getSolanaSupportedRoutesByNetwork(
sdkContext: SDKGlobalContext,
network: NetworkType,
): Promise<SolanaSupportedRoute[]> {
const cacheKey = `solana-${network}`
if (
sdkContext.evm.routesConfigCache != null &&
sdkContext.evm.routesConfigCache.get(cacheKey) != null
) {
return sdkContext.evm.routesConfigCache.get(cacheKey)!
}
const promise = _getSolanaSupportedRoutes(sdkContext, network).catch(err => {
const cachedPromise = sdkContext.evm.routesConfigCache?.get(cacheKey)
if (promise === cachedPromise) {
sdkContext.evm.routesConfigCache?.delete(cacheKey)
}
throw err
})
sdkContext.evm.routesConfigCache?.set(cacheKey, promise)
return promise
}
async function _getSolanaSupportedRoutes(
sdkContext: SDKGlobalContext,
network: NetworkType,
): Promise<SolanaSupportedRoute[]> {
const stacksChain =
network === "mainnet"
? KnownChainId.Stacks.Mainnet
: KnownChainId.Stacks.Testnet
const resp = await requestAPI<{ routes: SupportedSolanaBridgeRoute[] }>(
sdkContext,
{
method: "GET",
path: "/2024-10-01/solana/supported-routes",
query: {
network,
},
},
)
const routes = await Promise.all(
resp.routes.map(async (route): Promise<null | SolanaSupportedRoute> => {
const evmToken = route.evmToken as KnownTokenId.KnownToken
const stacksToken = await getStacksToken(
sdkContext,
stacksChain,
route.stacksTokenContractAddress,
)
if (stacksToken == null) return null
if (!KnownTokenId.isEVMToken(evmToken)) return null
return {
evmToken,
stacksChain,
stacksToken,
proxyStacksTokenContractAddress: route.proxyStacksTokenContractAddress,
pegOutFeeRate: BigNumber.from(route.pegOutFeeRate),
pegOutMinFeeAmount:
route.pegOutMinFeeAmount == null
? null
: BigNumber.from(route.pegOutMinFeeAmount),
pegOutMinAmount:
route.pegOutMinAmount == null
? null
: BigNumber.from(route.pegOutMinAmount),
pegOutMaxAmount:
route.pegOutMaxAmount == null
? null
: BigNumber.from(route.pegOutMaxAmount),
}
}),
)
return routes.filter(isNotNull)
}
interface SupportedSolanaBridgeRoute {
evmToken: string
stacksTokenContractAddress: StacksContractAddress
proxyStacksTokenContractAddress: null | StacksContractAddress
pegOutFeeRate: `${number}`
pegOutMinFeeAmount: null | `${number}`
pegOutMinAmount: null | `${number}`
pegOutMaxAmount: null | `${number}`
}

View File

@@ -0,0 +1,106 @@
import { getStacksToken } from "../../stacksUtils/contractHelpers"
import { requestAPI } from "../../utils/apiHelpers"
import { BigNumber } from "../../utils/BigNumber"
import { isNotNull } from "../../utils/typeHelpers"
import { KnownChainId, KnownTokenId } from "../../utils/types/knownIds"
import { StacksContractAddress } from "../../sdkUtils/types"
import { SDKGlobalContext } from "../../sdkUtils/types.internal"
import { TronSupportedRoute } from "../../tronUtils/types"
type NetworkType = "mainnet" | "testnet"
export async function getTronSupportedRoutes(
sdkContext: SDKGlobalContext,
): Promise<TronSupportedRoute[]> {
return getTronSupportedRoutesByNetwork(sdkContext, "mainnet" as NetworkType)
}
async function getTronSupportedRoutesByNetwork(
sdkContext: SDKGlobalContext,
network: NetworkType,
): Promise<TronSupportedRoute[]> {
const cacheKey = `tron-${network}`
if (
sdkContext.tron?.routesConfigCache != null &&
sdkContext.tron.routesConfigCache.get(cacheKey) != null
) {
return sdkContext.tron.routesConfigCache.get(cacheKey)!
}
const promise = _getTronSupportedRoutes(sdkContext, network).catch(err => {
const cachedPromise = sdkContext.tron?.routesConfigCache?.get(cacheKey)
if (promise === cachedPromise) {
sdkContext.tron?.routesConfigCache?.delete(cacheKey)
}
throw err
})
sdkContext.tron?.routesConfigCache?.set(cacheKey, promise)
return promise
}
async function _getTronSupportedRoutes(
sdkContext: SDKGlobalContext,
network: NetworkType,
): Promise<TronSupportedRoute[]> {
const stacksChain =
network === "mainnet"
? KnownChainId.Stacks.Mainnet
: KnownChainId.Stacks.Testnet
const resp = await requestAPI<{ routes: SupportedTronBridgeRoute[] }>(
sdkContext,
{
method: "GET",
path: "/2024-10-01/tron/supported-routes",
query: {
network,
},
},
)
const routes = await Promise.all(
resp.routes.map(async (route): Promise<null | TronSupportedRoute> => {
const tronToken = route.evmToken as KnownTokenId.KnownToken
const stacksToken = await getStacksToken(
sdkContext,
stacksChain,
route.stacksTokenContractAddress,
)
if (stacksToken == null) return null
if (!KnownTokenId.isTronToken(tronToken)) return null
return {
tronToken,
stacksChain,
stacksToken,
proxyStacksTokenContractAddress: route.proxyStacksTokenContractAddress,
pegOutFeeRate: BigNumber.from(route.pegOutFeeRate),
pegOutMinFeeAmount:
route.pegOutMinFeeAmount == null
? null
: BigNumber.from(route.pegOutMinFeeAmount),
pegOutMinAmount:
route.pegOutMinAmount == null
? null
: BigNumber.from(route.pegOutMinAmount),
pegOutMaxAmount:
route.pegOutMaxAmount == null
? null
: BigNumber.from(route.pegOutMaxAmount),
}
}),
)
return routes.filter(isNotNull)
}
interface SupportedTronBridgeRoute {
evmToken: string
stacksTokenContractAddress: StacksContractAddress
proxyStacksTokenContractAddress: null | StacksContractAddress
pegOutFeeRate: `${number}`
pegOutMinFeeAmount: null | `${number}`
pegOutMinAmount: null | `${number}`
pegOutMaxAmount: null | `${number}`
}

View File

@@ -9,6 +9,7 @@ import { pMemoizeImpl } from "../utils/pMemoize"
import { GeneralCacheInterface } from "../utils/types/GeneralCacheInterface"
import { KnownChainId } from "../utils/types/knownIds"
import { TransferProphet } from "../utils/types/TransferProphet"
import { TronSupportedRoute } from "../tronUtils/types"
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface SDKGlobalContextCache<K, V>
@@ -87,4 +88,10 @@ export interface SDKGlobalContext {
>
viemClients: Partial<Record<KnownChainId.EVMChain, Client>>
}
tron: {
routesConfigCache?: SDKGlobalContextCache<
"mainnet" | "testnet",
Promise<TronSupportedRoute[]>
>
}
}

14
src/tronUtils/types.ts Normal file
View File

@@ -0,0 +1,14 @@
import { KnownChainId, KnownTokenId } from "../utils/types/knownIds"
import { StacksContractAddress } from "../sdkUtils/types"
import { BigNumber } from "../utils/BigNumber"
export interface TronSupportedRoute {
tronToken: KnownTokenId.TronToken
stacksChain: KnownChainId.StacksChain
stacksToken: KnownTokenId.StacksToken
proxyStacksTokenContractAddress: null | StacksContractAddress
pegOutFeeRate: BigNumber
pegOutMinFeeAmount: null | BigNumber
pegOutMinAmount: null | BigNumber
pegOutMaxAmount: null | BigNumber
}