diff --git a/packages/router/src/lib/helpers/shared.ts b/packages/router/src/lib/helpers/shared.ts index 56e80037eb..f30d2eea93 100644 --- a/packages/router/src/lib/helpers/shared.ts +++ b/packages/router/src/lib/helpers/shared.ts @@ -15,6 +15,7 @@ export const getNtpTimeSeconds = async () => { /** * Helper to calculate router gas fee in token * + * @param sendingAssetId The asset address on source chain * @param sendingChainId The source chain Id * @param receivingAssetId The asset address on destination chain * @param receivingChainId The destination chain Id @@ -71,6 +72,47 @@ export const calculateGasFeeInReceivingToken = async ( return totalCost; }; + +/** + * Helper to calculate router gas fee in token for meta transaction + * + * @param receivingAssetId The asset address on destination chain + * @param receivingChainId The destination chain Id + * @param outputDecimals Decimal number of receiving asset + * @param requestContext Request context instance + */ +export const calculateGasFeeInReceivingTokenForFulfill = async ( + receivingAssetId: string, + receivingChainId: number, + outputDecimals: number, + requestContext: RequestContext, +): Promise => { + const chaindIdsForGasFee = getChainIdForGasFee(); + + if (!chaindIdsForGasFee.includes(receivingChainId)) return constants.Zero; + let totalCost = constants.Zero; + + if (chaindIdsForGasFee.includes(receivingChainId)) { + const gasLimitForFulfill = BigNumber.from(GAS_ESTIMATES.fulfill); + const ethPriceInReceivingChain = await getTokenPrice(receivingChainId, constants.AddressZero, requestContext); + const receivingTokenPrice = await getTokenPrice(receivingChainId, receivingAssetId, requestContext); + const gasPriceInReceivingChain = await getGasPrice(receivingChainId, requestContext); + + const gasAmountInUsd = gasPriceInReceivingChain.mul(gasLimitForFulfill).mul(ethPriceInReceivingChain); + const tokenAmountForGasFee = receivingTokenPrice.isZero() + ? constants.Zero + : gasAmountInUsd.div(receivingTokenPrice).div(BigNumber.from(10).pow(18 - outputDecimals)); + + console.log("gasLimitForFulfill = ", gasLimitForFulfill.toString()); + console.log("gasPriceInReceivingChain = ", gasPriceInReceivingChain.toString()); + console.log("ethPriceInReceivingChain = ", ethPriceInReceivingChain.toString()); + console.log("receivingTokenPrice = ", receivingTokenPrice.toString()); + totalCost = totalCost.add(tokenAmountForGasFee); + } + + return totalCost; +}; + /** * Gets token price in usd from price oracle * diff --git a/packages/router/src/lib/operations/fulfill.ts b/packages/router/src/lib/operations/fulfill.ts index 4c8c1925c5..7d2df29c26 100644 --- a/packages/router/src/lib/operations/fulfill.ts +++ b/packages/router/src/lib/operations/fulfill.ts @@ -11,6 +11,7 @@ import { getContext } from "../../router"; import { FulfillInput, FulfillInputSchema } from "../entities"; import { NoChainConfig, ParamsInvalid, NotEnoughRelayerFee } from "../errors"; import { NotAllowedFulfillRelay } from "../errors/fulfill"; +import { calculateGasFeeInReceivingTokenForFulfill } from "../helpers/shared"; export const fulfill = async ( invariantData: InvariantTransactionData, @@ -19,7 +20,7 @@ export const fulfill = async ( ): Promise => { const { requestContext, methodContext } = createLoggingContext(fulfill.name, _requestContext); - const { logger, contractWriter, config } = getContext(); + const { logger, contractWriter, config, chainData, txService } = getContext(); logger.info("Method start", requestContext, methodContext, { invariantData, input }); // Validate InvariantData schema @@ -73,9 +74,26 @@ export const fulfill = async ( }); } - const recvAmountLowerBound = BigNumber.from(amount) - .mul(100 - relayerFeeLowerBound) - .div(100); + let outputDecimals = chainData.get(invariantData.receivingChainId.toString())?.assetId[ + invariantData.receivingAssetId + ]?.decimals; + if (!outputDecimals) { + outputDecimals = await txService.getDecimalsForAsset( + invariantData.receivingChainId, + invariantData.receivingAssetId, + ); + } + logger.info("Got output decimals", requestContext, methodContext, { outputDecimals }); + const expectedFulfillFee = await calculateGasFeeInReceivingTokenForFulfill( + invariantData.receivingAssetId, + invariantData.receivingChainId, + outputDecimals, + requestContext, + ); + logger.info("Expected Fulfill fee in router side", requestContext, methodContext, { + expectedFulfillFee: expectedFulfillFee.toString(), + }); + const recvAmountLowerBound = expectedFulfillFee.mul(100 - relayerFeeLowerBound).div(100); if (BigNumber.from(amount).sub(input.relayerFee).lt(recvAmountLowerBound)) { throw new NotEnoughRelayerFee(fulfillChain, {