feat: upgrade evm fee model

This commit is contained in:
c4605
2025-06-26 15:27:15 +02:00
parent 5a14ffdfba
commit 847ceb990c
8 changed files with 124 additions and 111 deletions

View File

@@ -3,12 +3,12 @@ import {
defineContract, defineContract,
booleanT, booleanT,
responseSimpleT, responseSimpleT,
principalT,
bufferT,
uintT,
tupleT, tupleT,
uintT,
optionalT, optionalT,
bufferT,
listT, listT,
principalT,
traitT, traitT,
stringT, stringT,
noneT noneT
@@ -21,29 +21,11 @@ export const crossPegInEndpointV204Swap = defineContract({
output: responseSimpleT(booleanT, ), output: responseSimpleT(booleanT, ),
mode: 'public' mode: 'public'
}, },
callback: {
input: [
{ name: 'sender', type: principalT },
{ name: 'payload', type: bufferT }
],
output: responseSimpleT(booleanT, ),
mode: 'public'
},
'set-paused': { 'set-paused': {
input: [ { name: 'paused', type: booleanT } ], input: [ { name: 'paused', type: booleanT } ],
output: responseSimpleT(booleanT, ), output: responseSimpleT(booleanT, ),
mode: 'public' mode: 'public'
}, },
'set-peg-out-fee': {
input: [ { name: 'new-peg-out-fee', type: uintT } ],
output: responseSimpleT(booleanT, ),
mode: 'public'
},
'set-peg-out-gas-fee': {
input: [ { name: 'new-peg-out-gas-fee', type: uintT } ],
output: responseSimpleT(booleanT, ),
mode: 'public'
},
'transfer-to-cross-swap': { 'transfer-to-cross-swap': {
input: [ input: [
{ {
@@ -172,8 +154,6 @@ export const crossPegInEndpointV204Swap = defineContract({
mode: 'readonly' mode: 'readonly'
}, },
'get-paused': { input: [], output: booleanT, mode: 'readonly' }, 'get-paused': { input: [], output: booleanT, mode: 'readonly' },
'get-peg-out-fee': { input: [], output: uintT, mode: 'readonly' },
'get-peg-out-gas-fee': { input: [], output: uintT, mode: 'readonly' },
'get-required-validators': { input: [], output: uintT, mode: 'readonly' }, 'get-required-validators': { input: [], output: uintT, mode: 'readonly' },
'get-token-reserve-or-default': { 'get-token-reserve-or-default': {
input: [ input: [
@@ -269,8 +249,6 @@ export const crossPegInEndpointV204Swap = defineContract({
mode: 'mapEntry' mode: 'mapEntry'
}, },
'is-paused': { input: noneT, output: booleanT, mode: 'variable' }, 'is-paused': { input: noneT, output: booleanT, mode: 'variable' },
'peg-out-fee': { input: noneT, output: uintT, mode: 'variable' },
'peg-out-gas-fee': { input: noneT, output: uintT, mode: 'variable' },
'use-whitelist': { input: noneT, output: booleanT, mode: 'variable' } 'use-whitelist': { input: noneT, output: booleanT, mode: 'variable' }
} }
} as const) } as const)

View File

@@ -1,13 +1,17 @@
import { getEVMSupportedRoutes } from "../evmUtils/apiHelpers/getEVMSupportedRoutes" import { getEVMSupportedRoutes } from "../evmUtils/apiHelpers/getEVMSupportedRoutes"
import { getBRC20SupportedRoutes } from "../metaUtils/apiHelpers/getBRC20SupportedRoutes" import { getBRC20SupportedRoutes } from "../metaUtils/apiHelpers/getBRC20SupportedRoutes"
import { getRunesSupportedRoutes } from "../metaUtils/apiHelpers/getRunesSupportedRoutes" import { getRunesSupportedRoutes } from "../metaUtils/apiHelpers/getRunesSupportedRoutes"
import { StacksContractName } from "../stacksUtils/stxContractAddresses" import {
SDKGlobalContext,
withGlobalContextCache,
} from "../sdkUtils/types.internal"
import { import {
executeReadonlyCallBro, executeReadonlyCallBro,
getStacksContractCallInfo, getStacksContractCallInfo,
getStacksTokenContractInfo, getStacksTokenContractInfo,
numberFromStacksContractNumber, numberFromStacksContractNumber,
} from "../stacksUtils/contractHelpers" } from "../stacksUtils/contractHelpers"
import { StacksContractName } from "../stacksUtils/stxContractAddresses"
import { BigNumber } from "../utils/BigNumber" import { BigNumber } from "../utils/BigNumber"
import { import {
IsSupportedFn, IsSupportedFn,
@@ -19,7 +23,8 @@ import { props } from "../utils/promiseHelpers"
import { import {
getAndCheckTransitStacksTokens, getAndCheckTransitStacksTokens,
getSpecialFeeDetailsForSwapRoute, getSpecialFeeDetailsForSwapRoute,
SpecialFeeDetailsForSwapRoute, NormalizedSpecialFeeDetails,
normalizeSpecialFeeDetails,
SwapRoute, SwapRoute,
} from "../utils/SwapRouteHelpers" } from "../utils/SwapRouteHelpers"
import { checkNever, isNotNull } from "../utils/typeHelpers" import { checkNever, isNotNull } from "../utils/typeHelpers"
@@ -33,10 +38,6 @@ import {
TransferProphet_Fee_Fixed, TransferProphet_Fee_Fixed,
TransferProphet_Fee_Rate, TransferProphet_Fee_Rate,
} from "../utils/types/TransferProphet" } from "../utils/types/TransferProphet"
import {
SDKGlobalContext,
withGlobalContextCache,
} from "../sdkUtils/types.internal"
import { getBTCPegInAddress } from "./btcAddresses" import { getBTCPegInAddress } from "./btcAddresses"
export const getBtc2StacksFeeInfo = async ( export const getBtc2StacksFeeInfo = async (
@@ -235,9 +236,18 @@ const _getStacks2BtcFeeInfo = async (
console.log("[getStacks2BtcFeeInfo/specialFeeInfo]", route, specialFeeInfo) console.log("[getStacks2BtcFeeInfo/specialFeeInfo]", route, specialFeeInfo)
} }
const feeDetails: SpecialFeeDetailsForSwapRoute = const feeDetails: NormalizedSpecialFeeDetails =
specialFeeInfo ?? specialFeeInfo != null
(await props({ ? await normalizeSpecialFeeDetails(ctx, specialFeeInfo, {
getFeeRate: () =>
executeReadonlyCallBro(
stacksContractCallInfo.contractName,
"get-peg-out-fee",
{},
stacksContractCallInfo.executeOptions,
).then(numberFromStacksContractNumber),
})
: await props({
feeRate: executeReadonlyCallBro( feeRate: executeReadonlyCallBro(
stacksContractCallInfo.contractName, stacksContractCallInfo.contractName,
"get-peg-out-fee", "get-peg-out-fee",
@@ -250,7 +260,7 @@ const _getStacks2BtcFeeInfo = async (
{}, {},
stacksContractCallInfo.executeOptions, stacksContractCallInfo.executeOptions,
).then(numberFromStacksContractNumber), ).then(numberFromStacksContractNumber),
})) })
const resp = await props({ const resp = await props({
...feeDetails, ...feeDetails,

View File

@@ -28,7 +28,7 @@ export const contractNameOverrides_mainnet: Record<string, string> = {
"btc-peg-in-endpoint-v2-05-launchpad": "btc-peg-in-v2-05-launchpad-3c", "btc-peg-in-endpoint-v2-05-launchpad": "btc-peg-in-v2-05-launchpad-3c",
"meta-peg-in-endpoint-v2-06-swap": "meta-peg-in-v2-06e-swap", "meta-peg-in-endpoint-v2-06-swap": "meta-peg-in-v2-06e-swap",
"meta-peg-in-endpoint-v2-06-agg": "meta-peg-in-v2-06i-agg", "meta-peg-in-endpoint-v2-06-agg": "meta-peg-in-v2-06i-agg",
"cross-peg-in-endpoint-v2-04-swap": "cross-peg-in-v2-04b-swap", "cross-peg-in-endpoint-v2-04-swap": "cross-peg-in-v2-04c-swap",
"cross-peg-in-endpoint-v2-04-launchpad": "cross-peg-in-v2-04-launchpad-3c", "cross-peg-in-endpoint-v2-04-launchpad": "cross-peg-in-v2-04-launchpad-3c",
"cross-peg-out-endpoint-v2-01-agg": "cross-peg-out-v2-01c-agg", "cross-peg-out-endpoint-v2-01-agg": "cross-peg-out-v2-01c-agg",
} }

View File

@@ -15,6 +15,7 @@ import { BigNumber } from "../utils/BigNumber"
import { import {
getAndCheckTransitStacksTokens, getAndCheckTransitStacksTokens,
getSpecialFeeDetailsForSwapRoute, getSpecialFeeDetailsForSwapRoute,
normalizeSpecialFeeDetails,
} from "../utils/SwapRouteHelpers" } from "../utils/SwapRouteHelpers"
import { import {
IsSupportedFn, IsSupportedFn,
@@ -385,7 +386,16 @@ const _getStacks2EvmFeeInfo = async (
reserve, reserve,
]) ])
const feeRate = numberFromStacksContractNumber(tokenConf.fee)
const minFee = numberFromStacksContractNumber(tokenConf["min-fee"])
if (specialFeeInfo != null) { if (specialFeeInfo != null) {
const normalizedFeeDetails = await normalizeSpecialFeeDetails(
ctx,
specialFeeInfo,
{ getFeeRate: async () => feeRate },
)
return { return {
isPaused, isPaused,
bridgeToken: route.fromToken, bridgeToken: route.fromToken,
@@ -393,29 +403,26 @@ const _getStacks2EvmFeeInfo = async (
{ {
type: "rate", type: "rate",
token: route.fromToken, token: route.fromToken,
rate: specialFeeInfo.feeRate, rate: normalizedFeeDetails.feeRate,
minimumAmount: specialFeeInfo.minFeeAmount, minimumAmount: normalizedFeeDetails.minFeeAmount,
}, },
...(specialFeeInfo.gasFee == null ...(normalizedFeeDetails.gasFee == null
? [] ? []
: [ : [
{ {
type: "fixed", type: "fixed",
token: specialFeeInfo.gasFee.token, token: normalizedFeeDetails.gasFee.token,
amount: specialFeeInfo.gasFee.amount, amount: normalizedFeeDetails.gasFee.amount,
} satisfies TransferProphet_Fee_Fixed, } satisfies TransferProphet_Fee_Fixed,
]), ]),
], ],
minBridgeAmount: BigNumber.isZero(minAmount) minBridgeAmount: BigNumber.isZero(minAmount)
? specialFeeInfo.minFeeAmount ? normalizedFeeDetails.minFeeAmount
: BigNumber.max([minAmount, specialFeeInfo.minFeeAmount]), : BigNumber.max([minAmount, normalizedFeeDetails.minFeeAmount]),
maxBridgeAmount: maxAmount, maxBridgeAmount: maxAmount,
} }
} }
const feeRate = numberFromStacksContractNumber(tokenConf.fee)
const minFee = numberFromStacksContractNumber(tokenConf["min-fee"])
return { return {
isPaused, isPaused,
bridgeToken: route.fromToken, bridgeToken: route.fromToken,

View File

@@ -9,6 +9,8 @@ import { BigNumber } from "../utils/BigNumber"
import { import {
getAndCheckTransitStacksTokens, getAndCheckTransitStacksTokens,
getSpecialFeeDetailsForSwapRoute, getSpecialFeeDetailsForSwapRoute,
NormalizedSpecialFeeDetails,
normalizeSpecialFeeDetails,
SwapRoute, SwapRoute,
} from "../utils/SwapRouteHelpers" } from "../utils/SwapRouteHelpers"
import { import {
@@ -315,9 +317,12 @@ const _getStacks2MetaFeeInfo = async (
console.log("[getStacks2MetaFeeInfo/specialFeeInfo]", route, specialFeeInfo) console.log("[getStacks2MetaFeeInfo/specialFeeInfo]", route, specialFeeInfo)
} }
const feeDetails = const feeDetails: NormalizedSpecialFeeDetails =
specialFeeInfo ?? specialFeeInfo != null
(await props({ ? await normalizeSpecialFeeDetails(ctx, specialFeeInfo, {
getFeeRate: async () => filteredRoute.pegOutFeeRate,
})
: await props({
feeRate: filteredRoute.pegOutFeeRate, feeRate: filteredRoute.pegOutFeeRate,
minFeeAmount: BigNumber.ZERO, minFeeAmount: BigNumber.ZERO,
gasFee: gasFee:
@@ -327,7 +332,7 @@ const _getStacks2MetaFeeInfo = async (
token: KnownTokenId.Stacks.aBTC, token: KnownTokenId.Stacks.aBTC,
amount: filteredRoute.pegOutFeeBitcoinAmount, amount: filteredRoute.pegOutFeeBitcoinAmount,
}), }),
})) })
if (ctx.debugLog) { if (ctx.debugLog) {
console.log("[getStacks2MetaFeeInfo]", route, feeDetails) console.log("[getStacks2MetaFeeInfo]", route, feeDetails)
} }

View File

@@ -10,6 +10,7 @@ import { GeneralCacheInterface } from "../utils/types/GeneralCacheInterface"
import { KnownChainId } from "../utils/types/knownIds" import { KnownChainId } from "../utils/types/knownIds"
import { TransferProphet } from "../utils/types/TransferProphet" import { TransferProphet } from "../utils/types/TransferProphet"
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface SDKGlobalContextCache<K, V> export interface SDKGlobalContextCache<K, V>
extends GeneralCacheInterface<K, V> {} extends GeneralCacheInterface<K, V> {}

View File

@@ -15,6 +15,9 @@ export const contractsMultisigMainnet =
export const contractsMultisigTestnet = export const contractsMultisigTestnet =
"ST2QXSK64YQX3CQPC530K79XWQ98XFAM9W3XKEH3N" "ST2QXSK64YQX3CQPC530K79XWQ98XFAM9W3XKEH3N"
export const contractsDeployerMainnet_v2 =
"SP2FGHFNGMH6NF2427Y20271Q6CKJ67FDX2V2JG6X"
export const alexContractDeployerMainnet = export const alexContractDeployerMainnet =
"SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM" "SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM"
export const alexContractDeployerTestnet = export const alexContractDeployerTestnet =
@@ -128,7 +131,7 @@ export const stxContractAddresses = {
}, },
[StacksContractName.EVMPegInEndpointSwap]: { [StacksContractName.EVMPegInEndpointSwap]: {
[KnownChainId.Stacks.Mainnet]: wrapContractAddress("mainnet", { [KnownChainId.Stacks.Mainnet]: wrapContractAddress("mainnet", {
deployerAddress: contractsMultisigMainnet, deployerAddress: contractsDeployerMainnet_v2,
contractName: StacksContractName.EVMPegInEndpointSwap, contractName: StacksContractName.EVMPegInEndpointSwap,
}), }),
[KnownChainId.Stacks.Testnet]: wrapContractAddress("testnet", { [KnownChainId.Stacks.Testnet]: wrapContractAddress("testnet", {

View File

@@ -1,15 +1,15 @@
import { EVM_BARE_PEG_IN_USE_SWAP_CONTRACT } from "../config" import { EVM_BARE_PEG_IN_USE_SWAP_CONTRACT } from "../config"
import { evmTokenToCorrespondingStacksToken } from "../evmUtils/peggingHelpers" import { evmTokenToCorrespondingStacksToken } from "../evmUtils/peggingHelpers"
import { metaTokenToCorrespondingStacksToken } from "../metaUtils/peggingHelpers" import { metaTokenToCorrespondingStacksToken } from "../metaUtils/peggingHelpers"
import { StacksContractName } from "../stacksUtils/stxContractAddresses" import { SDKNumber, StacksContractAddress } from "../sdkUtils/types"
import { SDKGlobalContext } from "../sdkUtils/types.internal"
import { import {
executeReadonlyCallBro, executeReadonlyCallBro,
getStacksContractCallInfo, getStacksContractCallInfo,
getStacksToken, getStacksToken,
numberFromStacksContractNumber, numberFromStacksContractNumber,
} from "../stacksUtils/contractHelpers" } from "../stacksUtils/contractHelpers"
import { SDKNumber, StacksContractAddress } from "../sdkUtils/types" import { StacksContractName } from "../stacksUtils/stxContractAddresses"
import { SDKGlobalContext } from "../sdkUtils/types.internal"
import { last } from "./arrayHelpers" import { last } from "./arrayHelpers"
import { BigNumber } from "./BigNumber" import { BigNumber } from "./BigNumber"
import { import {
@@ -327,8 +327,9 @@ export async function toCorrespondingStacksToken(
return toStacksTokenPromise return toStacksTokenPromise
} }
const inheritFeeInfoSymbol = Symbol("inheritFeeInfo")
export interface SpecialFeeDetailsForSwapRoute { export interface SpecialFeeDetailsForSwapRoute {
feeRate: BigNumber feeRate: BigNumber | typeof inheritFeeInfoSymbol
minFeeAmount: BigNumber minFeeAmount: BigNumber
gasFee?: { token: KnownTokenId.KnownToken; amount: BigNumber } gasFee?: { token: KnownTokenId.KnownToken; amount: BigNumber }
} }
@@ -381,22 +382,14 @@ export async function getSpecialFeeDetailsForSwapRoute(
return getFeeInfo( return getFeeInfo(
{ {
fromEVM: { fromEVM:
getFeeRate: () => KnownChainId.isBRC20Chain(route.toChain) ||
executeReadonlyCallBro( KnownChainId.isRunesChain(route.toChain)
evmPegInContractCallInfo.contractName, ? {
"get-peg-out-fee", getFeeRate: async () => inheritFeeInfoSymbol,
{}, getFixedFeeAmount: async () => BigNumber.ZERO,
evmPegInContractCallInfo.executeOptions, }
).then(numberFromStacksContractNumber), : undefined,
getFixedFeeAmount: () =>
executeReadonlyCallBro(
evmPegInContractCallInfo.contractName,
"get-peg-out-gas-fee",
{},
evmPegInContractCallInfo.executeOptions,
).then(numberFromStacksContractNumber),
},
}, },
{ {
initialRoute: options.initialRoute, initialRoute: options.initialRoute,
@@ -458,22 +451,14 @@ export async function getSpecialFeeDetailsForSwapRoute(
metaPegInSwapContractCallInfo.executeOptions, metaPegInSwapContractCallInfo.executeOptions,
).then(numberFromStacksContractNumber), ).then(numberFromStacksContractNumber),
}, },
fromEVM: { fromEVM:
getFeeRate: () => KnownChainId.isBRC20Chain(route.toChain) ||
executeReadonlyCallBro( KnownChainId.isRunesChain(route.toChain)
evmPegInSwapContractCallInfo.contractName, ? {
"get-peg-out-fee", getFeeRate: async () => inheritFeeInfoSymbol,
{}, getFixedFeeAmount: async () => BigNumber.ZERO,
evmPegInSwapContractCallInfo.executeOptions, }
).then(numberFromStacksContractNumber), : undefined,
getFixedFeeAmount: () =>
executeReadonlyCallBro(
evmPegInSwapContractCallInfo.contractName,
"get-peg-out-gas-fee",
{},
evmPegInSwapContractCallInfo.executeOptions,
).then(numberFromStacksContractNumber),
},
}, },
{ {
initialRoute: options.initialRoute, initialRoute: options.initialRoute,
@@ -491,15 +476,15 @@ export async function getSpecialFeeDetailsForSwapRoute(
async function getFeeInfo( async function getFeeInfo(
context: { context: {
fromBitcoin?: { fromBitcoin?: {
getFeeRate: () => Promise<BigNumber> getFeeRate: () => Promise<BigNumber | typeof inheritFeeInfoSymbol>
getFixedFeeAmount: () => Promise<BigNumber> getFixedFeeAmount: () => Promise<BigNumber>
} }
fromMeta?: { fromMeta?: {
getFeeRate: () => Promise<BigNumber> getFeeRate: () => Promise<BigNumber | typeof inheritFeeInfoSymbol>
getFixedFeeAmount: () => Promise<BigNumber> getFixedFeeAmount: () => Promise<BigNumber>
} }
fromEVM?: { fromEVM?: {
getFeeRate: () => Promise<BigNumber> getFeeRate: () => Promise<BigNumber | typeof inheritFeeInfoSymbol>
getFixedFeeAmount: () => Promise<BigNumber> getFixedFeeAmount: () => Promise<BigNumber>
} }
}, },
@@ -618,3 +603,27 @@ export async function getSpecialFeeDetailsForSwapRoute(
return feeInfo return feeInfo
} }
} }
export interface NormalizedSpecialFeeDetails {
feeRate: BigNumber
minFeeAmount: BigNumber
gasFee?: { token: KnownTokenId.KnownToken; amount: BigNumber }
}
export async function normalizeSpecialFeeDetails(
ctx: SDKGlobalContext,
rawFeeDetails: SpecialFeeDetailsForSwapRoute,
getters: {
getFeeRate: () => Promise<BigNumber>
},
): Promise<NormalizedSpecialFeeDetails> {
const feeRate =
rawFeeDetails.feeRate === inheritFeeInfoSymbol
? await getters.getFeeRate()
: rawFeeDetails.feeRate
return {
feeRate,
minFeeAmount: rawFeeDetails.minFeeAmount,
gasFee: rawFeeDetails.gasFee,
}
}