diff --git a/package.json b/package.json index aa064cf90..8e9f67c13 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "dependencies": { "@across-protocol/constants": "^3.1.25", "@across-protocol/contracts": "^3.0.19", - "@across-protocol/sdk": "^3.3.32", + "@across-protocol/sdk": "^3.4.5", "@arbitrum/sdk": "^4.0.2", "@consensys/linea-sdk": "^0.2.1", "@defi-wonderland/smock": "^2.3.5", diff --git a/scripts/zkSyncDemo.ts b/scripts/zkSyncDemo.ts index 34d5aec6c..5edb3e5c7 100644 --- a/scripts/zkSyncDemo.ts +++ b/scripts/zkSyncDemo.ts @@ -62,7 +62,7 @@ export async function run(): Promise { connectedSigner ); const l2PubdataByteLimit = zksync.utils.REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT; - const l1GasPriceData = await gasPriceOracle.getGasPriceEstimate(l1Provider, l1ChainId); + const l1GasPriceData = await gasPriceOracle.getGasPriceEstimate(l1Provider, { chainId: l1ChainId }); const estimatedL1GasPrice = l1GasPriceData.maxPriorityFeePerGas.add(l1GasPriceData.maxFeePerGas); // The ZkSync Mailbox contract checks that the msg.value of the transaction is enough to cover the transaction base // cost. The transaction base cost can be queried from the Mailbox by passing in an L1 "executed" gas price, diff --git a/src/clients/ProfitClient.ts b/src/clients/ProfitClient.ts index 33e66197b..d7739671b 100644 --- a/src/clients/ProfitClient.ts +++ b/src/clients/ProfitClient.ts @@ -29,6 +29,7 @@ import { TOKEN_EQUIVALENCE_REMAPPING, ZERO_ADDRESS, formatGwei, + fixedPointAdjustment, } from "../utils"; import { Deposit, DepositWithBlock, L1Token, SpokePoolClientsByChain } from "../interfaces"; import { getAcrossHost } from "./AcrossAPIClient"; @@ -636,24 +637,49 @@ export class ProfitClient { }; // Pre-fetch total gas costs for relays on enabled chains. - await sdkUtils.mapAsync(enabledChainIds, async (destinationChainId) => { - const symbol = testSymbols[destinationChainId] ?? defaultTestSymbol; - const hubToken = TOKEN_SYMBOLS_MAP[symbol].addresses[this.hubPoolClient.chainId]; - const outputToken = - destinationChainId === hubPoolClient.chainId - ? hubToken - : hubPoolClient.getL2TokenForL1TokenAtBlock(hubToken, destinationChainId); - assert(isDefined(outputToken), `Chain ${destinationChainId} SpokePool is not configured for ${symbol}`); - - const deposit = { ...sampleDeposit, destinationChainId, outputToken }; - this.totalGasCosts[destinationChainId] = await this._getTotalGasCost(deposit, relayer); - }); + const totalGasCostsToLog = Object.fromEntries( + await sdkUtils.mapAsync(enabledChainIds, async (destinationChainId) => { + const symbol = testSymbols[destinationChainId] ?? defaultTestSymbol; + const hubToken = TOKEN_SYMBOLS_MAP[symbol].addresses[this.hubPoolClient.chainId]; + const outputToken = + destinationChainId === hubPoolClient.chainId + ? hubToken + : hubPoolClient.getL2TokenForL1TokenAtBlock(hubToken, destinationChainId); + assert(isDefined(outputToken), `Chain ${destinationChainId} SpokePool is not configured for ${symbol}`); + + const deposit = { ...sampleDeposit, destinationChainId, outputToken }; + const gasCosts = await this._getTotalGasCost(deposit, relayer); + // The scaledNativeGasCost is approximately what the relayer will set as the `gasLimit` when submitting + // fills on the destination chain. + const scaledNativeGasCost = gasCosts.nativeGasCost.mul(this.gasPadding).div(fixedPointAdjustment); + // The scaledTokenGasCost is the estimated gas cost of submitting a fill on the destination chain and is used + // in the this.estimateFillCost function to determine whether a deposit is profitable to fill. Therefore, + // the scaledTokenGasCost should be safely lower than the quote API's tokenGasCosts in order for the relayer + // to consider a deposit is profitable. + const scaledTokenGasCost = gasCosts.tokenGasCost + .mul(this.gasPadding) + .div(fixedPointAdjustment) + .mul(this.gasMultiplier) + .div(fixedPointAdjustment); + this.totalGasCosts[destinationChainId] = gasCosts; + return [ + destinationChainId, + { + ...gasCosts, + scaledNativeGasCost, + scaledTokenGasCost, + gasPadding: formatEther(this.gasPadding), + gasMultiplier: formatEther(this.gasMultiplier), + }, + ]; + }) + ); this.logger.debug({ at: "ProfitClient", message: "Updated gas cost", enabledChainIds: this.enabledChainIds, - totalGasCosts: this.totalGasCosts, + totalGasCosts: totalGasCostsToLog, }); } diff --git a/src/utils/TransactionUtils.ts b/src/utils/TransactionUtils.ts index a8b0fa1ea..bc6c81043 100644 --- a/src/utils/TransactionUtils.ts +++ b/src/utils/TransactionUtils.ts @@ -8,7 +8,6 @@ import { BigNumber, bnZero, Contract, - fixedPointAdjustment as fixedPoint, isDefined, TransactionResponse, ethers, @@ -82,7 +81,12 @@ export async function runTransaction( Number(process.env[`MAX_FEE_PER_GAS_SCALER_${chainId}`] || process.env.MAX_FEE_PER_GAS_SCALER) || DEFAULT_GAS_FEE_SCALERS[chainId]?.maxFeePerGasScaler; - const gas = await getGasPrice(provider, priorityFeeScaler, maxFeePerGasScaler); + const gas = await getGasPrice( + provider, + priorityFeeScaler, + maxFeePerGasScaler, + await contract.populateTransaction[method](...(args as Array), { value }) + ); logger.debug({ at: "TxUtil", @@ -154,30 +158,30 @@ export async function runTransaction( } } -// TODO: add in gasPrice when the SDK has this for the given chainId. TODO: improve how we fetch prices. -// For now this method will extract the provider's Fee data from the associated network and scale it by a priority -// scaler. This works on both mainnet and L2's by the utility switching the response structure accordingly. export async function getGasPrice( provider: ethers.providers.Provider, priorityScaler = 1.2, - maxFeePerGasScaler = 3 + maxFeePerGasScaler = 3, + transactionObject?: ethers.PopulatedTransaction ): Promise> { + // Floor scalers at 1.0 as we'll rarely want to submit too low of a gas price. We mostly + // just want to submit with as close to prevailing fees as possible. + maxFeePerGasScaler = Math.max(1, maxFeePerGasScaler); + priorityScaler = Math.max(1, priorityScaler); const { chainId } = await provider.getNetwork(); - const feeData = await gasPriceOracle.getGasPriceEstimate(provider, chainId); - - if (feeData.maxPriorityFeePerGas.gt(feeData.maxFeePerGas)) { - feeData.maxFeePerGas = scaleByNumber(feeData.maxPriorityFeePerGas, 1.5); - } - - // Handle chains with legacy pricing. - if (feeData.maxPriorityFeePerGas.eq(bnZero)) { - return { gasPrice: scaleByNumber(feeData.maxFeePerGas, priorityScaler) }; - } - - // Default to EIP-1559 (type 2) pricing. + // Pass in unsignedTx here for better Linea gas price estimations via the Linea Viem provider. + const feeData = await gasPriceOracle.getGasPriceEstimate(provider, { + chainId, + baseFeeMultiplier: toBNWei(maxFeePerGasScaler), + priorityFeeMultiplier: toBNWei(priorityScaler), + unsignedTx: transactionObject, + }); + + // Default to EIP-1559 (type 2) pricing. If gasPriceOracle is using a legacy adapter for this chain then + // the priority fee will be 0. return { - maxFeePerGas: scaleByNumber(feeData.maxFeePerGas, Math.max(priorityScaler * maxFeePerGasScaler, 1)), - maxPriorityFeePerGas: scaleByNumber(feeData.maxPriorityFeePerGas, priorityScaler), + maxFeePerGas: feeData.maxFeePerGas, + maxPriorityFeePerGas: feeData.maxPriorityFeePerGas, }; } @@ -231,7 +235,3 @@ export function getTarget(targetAddress: string): return { targetAddress }; } } - -function scaleByNumber(amount: BigNumber, scaling: number) { - return amount.mul(toBNWei(scaling)).div(fixedPoint); -} diff --git a/yarn.lock b/yarn.lock index c2e2d330e..6934fca1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -58,10 +58,10 @@ yargs "^17.7.2" zksync-web3 "^0.14.3" -"@across-protocol/sdk@^3.3.32": - version "3.3.32" - resolved "https://registry.yarnpkg.com/@across-protocol/sdk/-/sdk-3.3.32.tgz#fa2428df5f9b6cb0392c46f742f11265efa4abb3" - integrity sha512-ADyZQeWxjGAreLoeVQYNiJN4zMmmJ7h6ItgbSjP2+JvZENPaH9t23xCegPIyI0oiVqLrOHOGCJ/yEdX6X3HqpQ== +"@across-protocol/sdk@^3.4.5": + version "3.4.5" + resolved "https://registry.yarnpkg.com/@across-protocol/sdk/-/sdk-3.4.5.tgz#343f53be92e92afd2fd4dbea16f180533093ea26" + integrity sha512-o1DovRfSriL0GTa304rd2KcBDwWYbK2SHT8mElyCLg6beX5RnJKT0YiMUMuCMfZ7MZJscdGzV023e5BhbyeP5A== dependencies: "@across-protocol/across-token" "^1.0.0" "@across-protocol/constants" "^3.1.25"