Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions sdk/packages/sdk/src/protocols/intents/GasEstimator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import type { HexString } from "@/types"
import type { IntentGatewayContext } from "./types"
import { BundlerMethod } from "./types"
import type { BundlerGasEstimate, PimlicoGasPriceEstimate } from "./types"
import { getFeeToken, transformOrderForContract, convertGasToFeeToken } from "./utils"
import { getFeeToken, transformOrderForContract, convertGasToFeeToken, convertFeeTokenToWei } from "./utils"
import { CryptoUtils } from "./CryptoUtils"

/**
Expand Down Expand Up @@ -241,17 +241,15 @@ export class GasEstimator {
preVerificationGas = (BigInt(gasEstimate.preVerificationGas) * 105n) / 100n

if (pimlicoGasPrices) {
const level =
pimlicoGasPrices.fast ?? pimlicoGasPrices.standard ?? pimlicoGasPrices.slow ?? null
const level = pimlicoGasPrices.fast ?? pimlicoGasPrices.standard ?? pimlicoGasPrices.slow ?? null

if (level) {
const pimMaxFeePerGas = BigInt(level.maxFeePerGas)
const pimMaxPriorityFeePerGas = BigInt(level.maxPriorityFeePerGas)

maxFeePerGas = pimMaxFeePerGas + (pimMaxFeePerGas * BigInt(maxFeeBumpPercent)) / 100n
maxPriorityFeePerGas =
pimMaxPriorityFeePerGas +
(pimMaxPriorityFeePerGas * BigInt(priorityFeeBumpPercent)) / 100n
pimMaxPriorityFeePerGas + (pimMaxPriorityFeePerGas * BigInt(priorityFeeBumpPercent)) / 100n
}
}
} catch (e) {
Expand All @@ -275,19 +273,22 @@ export class GasEstimator {
}

const totalGas = callGasLimit + verificationGasLimit + preVerificationGas
const totalGasCostWei = totalGas * maxFeePerGas
const rawTotalGasCostWei = totalGas * maxFeePerGas

const totalGasInDestFeeToken = await convertGasToFeeToken(
this.ctx,
totalGas,
"dest",
destStateMachineId,
gasPrice,
)
const totalGasInSourceFeeToken = adjustDecimals(
totalGasInDestFeeToken,
destFeeToken.decimals,
sourceFeeToken.decimals,
)
const totalGasInSourceFeeToken = isSameChain
? totalGasInDestFeeToken
: adjustDecimals(totalGasInDestFeeToken, destFeeToken.decimals, sourceFeeToken.decimals)

const totalGasCostWei = isSameChain
? rawTotalGasCostWei
: await convertFeeTokenToWei(this.ctx, totalGasInSourceFeeToken, "source", souceStateMachineId)

return {
callGasLimit,
Expand Down
47 changes: 47 additions & 0 deletions sdk/packages/sdk/src/protocols/intents/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,50 @@ export async function convertGasToFeeToken(
return parseUnits(gasCostInFeeToken.toFixed(feeToken.decimals), feeToken.decimals)
}
}

/**
* Converts a fee-token amount on a given chain into the equivalent native wei amount.
*
* First attempts a Uniswap V2 quote (fee token -> WETH). If that quote fails or
* returns zero, falls back to a price-oracle estimate assuming the fee token is $1.
*
* @param ctx - Shared IntentsV2 context.
* @param feeTokenAmount - Amount in fee token units (scaled by fee token decimals).
* @param feeTokenIn - Which chain side the fee-token amount belongs to (`"source"` or `"dest"`).
* @param evmChainID - State-machine ID of the chain used for conversion.
* @returns Native wei-equivalent amount.
*/
export async function convertFeeTokenToWei(
ctx: IntentGatewayContext,
feeTokenAmount: bigint,
feeTokenIn: "source" | "dest",
evmChainID: string,
): Promise<bigint> {
const chain = ctx[feeTokenIn]
const client = chain.client
const wethAddr = chain.configService.getWrappedNativeAssetWithDecimals(evmChainID).asset
const feeToken = await getFeeToken(ctx, evmChainID, chain)

try {
const { amountOut } = await ctx.swap.findBestProtocolWithAmountIn(
client,
feeToken.address,
wethAddr,
feeTokenAmount,
evmChainID,
{ selectedProtocol: "v2" },
)
if (amountOut === 0n) {
throw new Error()
}
return amountOut
} catch {
const nativeCurrency = client.chain?.nativeCurrency
const chainId = Number.parseInt(evmChainID.split("-")[1])
const feeTokenAmountInToken = new Decimal(formatUnits(feeTokenAmount, feeToken.decimals))
const nativeTokenPriceUsd = await fetchPrice(nativeCurrency?.symbol, chainId)
const feeTokenAmountUsd = feeTokenAmountInToken.times(new Decimal(1))
const nativeAmount = feeTokenAmountUsd.dividedBy(nativeTokenPriceUsd)
return parseUnits(nativeAmount.toFixed(nativeCurrency?.decimals ?? 18), nativeCurrency?.decimals ?? 18)
}
}
4 changes: 4 additions & 0 deletions sdk/packages/sdk/src/tests/sequential/intentGateway.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,10 @@ async function runCrossChainEstimate(srcKey: string, destKey: string) {
const estimate = await intentGateway.estimateFillOrder({ order })

console.log(`${srcKey} => ${destKey}`)
console.log("Estimated cost (totalGasCostWei):", estimate.totalGasCostWei)
console.log("Estimated fee (totalGasInFeeToken):", estimate.totalGasInFeeToken)

assert(estimate.totalGasCostWei > 0n)
assert(estimate.totalGasInFeeToken > 0n)
}

Expand All @@ -138,6 +140,8 @@ async function runSameChainEstimate(chainKey: string) {

const estimate = await intentGateway.estimateFillOrder({ order })

console.log(`${chainKey} same-chain estimated cost (totalGasCostWei):`, estimate.totalGasCostWei)
console.log(`${chainKey} same-chain USDC => EXT, estimated fee:`, estimate.totalGasInFeeToken)
assert(estimate.totalGasCostWei > 0n)
assert(estimate.totalGasInFeeToken > 0n)
}
Original file line number Diff line number Diff line change
Expand Up @@ -263,15 +263,6 @@ describe("Filler V2 - Solver Selection ON", () => {
console.log(`Order placed successfully with ID: ${order.id}`)
}

for await (const status of gen) {
if (status.status === "BID_SELECTED") {
console.log(`Bid selected: solver=${status.selectedSolver}, userOpHash=${status.userOpHash}`)
}
if (status.status === "FAILED") {
throw new Error(`Order execution failed: ${status.error}`)
}
}

expect(order.id).toBeDefined()
expect(order.user).toBe(bytes20ToBytes32(beneficiaryAddress))
expect(order.source).toBe(toHex(bscChapelId))
Expand Down
Loading