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,
booleanT,
responseSimpleT,
principalT,
bufferT,
uintT,
tupleT,
uintT,
optionalT,
bufferT,
listT,
principalT,
traitT,
stringT,
noneT
@@ -21,29 +21,11 @@ export const crossPegInEndpointV204Swap = defineContract({
output: responseSimpleT(booleanT, ),
mode: 'public'
},
callback: {
input: [
{ name: 'sender', type: principalT },
{ name: 'payload', type: bufferT }
],
output: responseSimpleT(booleanT, ),
mode: 'public'
},
'set-paused': {
input: [ { name: 'paused', type: booleanT } ],
output: responseSimpleT(booleanT, ),
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': {
input: [
{
@@ -172,8 +154,6 @@ export const crossPegInEndpointV204Swap = defineContract({
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-token-reserve-or-default': {
input: [
@@ -269,8 +249,6 @@ export const crossPegInEndpointV204Swap = defineContract({
mode: 'mapEntry'
},
'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' }
}
} as const)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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