diff --git a/src/providers/v3/gas-data-provider.ts b/src/providers/v3/gas-data-provider.ts index 1da06eec5..0ad95c74d 100644 --- a/src/providers/v3/gas-data-provider.ts +++ b/src/providers/v3/gas-data-provider.ts @@ -121,9 +121,10 @@ export class ArbitrumGasDataProvider this.provider ); const gasData = await gasDataContract.getPricesInWei(); + const perL1CalldataByte = gasData[1]; return { perL2TxFee: gasData[0], - perL1CalldataFee: gasData[1], + perL1CalldataFee: perL1CalldataByte.div(16), perArbGasTotal: gasData[5], }; } diff --git a/src/routers/alpha-router/gas-models/v3/v3-heuristic-gas-model.ts b/src/routers/alpha-router/gas-models/v3/v3-heuristic-gas-model.ts index 05063e948..c7b187f94 100644 --- a/src/routers/alpha-router/gas-models/v3/v3-heuristic-gas-model.ts +++ b/src/routers/alpha-router/gas-models/v3/v3-heuristic-gas-model.ts @@ -13,7 +13,10 @@ import { OptimismGasData, } from '../../../../providers/v3/gas-data-provider'; import { CurrencyAmount } from '../../../../util/amounts'; -import { getL2ToL1GasUsed } from '../../../../util/gas-factory-helpers'; +import { + calculateArbitrumToL1FeeFromCalldata, + getL2ToL1GasUsed +} from '../../../../util/gas-factory-helpers'; import { log } from '../../../../util/log'; import { buildSwapMethodParameters, @@ -419,7 +422,6 @@ export class V3HeuristicGasModelFactory extends IOnChainGasModelFactory { swapConfig: SwapOptionsUniversalRouter, gasData: ArbitrumGasData ): [BigNumber, BigNumber] { - const { perL2TxFee, perL1CalldataFee } = gasData; const route: V3RouteWithValidQuote = routes[0]!; @@ -439,11 +441,6 @@ export class V3HeuristicGasModelFactory extends IOnChainGasModelFactory { swapConfig, ChainId.ARBITRUM_ONE ).calldata; - // calculates gas amounts based on bytes of calldata, use 0 as overhead. - const l1GasUsed = getL2ToL1GasUsed(data, BigNumber.from(0)); - // multiply by the fee per calldata and add the flat l2 fee - let l1Fee = l1GasUsed.mul(perL1CalldataFee); - l1Fee = l1Fee.add(perL2TxFee); - return [l1GasUsed, l1Fee]; + return calculateArbitrumToL1FeeFromCalldata(data, gasData); } } diff --git a/src/util/gas-factory-helpers.ts b/src/util/gas-factory-helpers.ts index 1c6ddc6b0..643aeeb88 100644 --- a/src/util/gas-factory-helpers.ts +++ b/src/util/gas-factory-helpers.ts @@ -250,12 +250,9 @@ export async function calculateGasUsed( const gasPriceWei = route.gasPriceWei; // calculate L2 to L1 security fee if relevant let l2toL1FeeInWei = BigNumber.from(0); - if ([ChainId.ARBITRUM_ONE, ChainId.ARBITRUM_GOERLI].includes(chainId)) { - l2toL1FeeInWei = calculateArbitrumToL1FeeFromCalldata( - route.methodParameters!.calldata, - l2GasData as ArbitrumGasData - )[1]; - } else if ( + // Arbitrum charges L2 gas for L1 calldata posting costs. + // See https://github.com/Uniswap/smart-order-router/pull/464/files#r1441376802 + if ( [ ChainId.OPTIMISM, ChainId.OPTIMISM_GOERLI, diff --git a/test/unit/providers/v3/gas-data-provider.test.ts b/test/unit/providers/v3/gas-data-provider.test.ts new file mode 100644 index 000000000..67e40ec55 --- /dev/null +++ b/test/unit/providers/v3/gas-data-provider.test.ts @@ -0,0 +1,29 @@ +import { BigNumber } from '@ethersproject/bignumber'; +import { ArbitrumGasDataProvider } from '../../../../src/providers/v3/gas-data-provider'; +import { BaseProvider } from '@ethersproject/providers'; +import sinon from 'sinon'; + +class MockProvider extends BaseProvider { + _isProvider: boolean = true +} + +describe('arbitrum gas data provider', () => { + + let mockProvider: sinon.SinonStubbedInstance; + let arbGasDataProvider: ArbitrumGasDataProvider; + + beforeAll(() => { + mockProvider = sinon.createStubInstance(MockProvider) + mockProvider._isProvider = true + mockProvider.call.resolves("0x00000000000000000000000000000000000000000000000000003eb61132144000000000000000000000000000000000000000000000000000000072ac022fb0000000000000000000000000000000000000000000000000000001d1a94a20000000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f5e100"); + arbGasDataProvider = new ArbitrumGasDataProvider(42161, mockProvider) + }); + + test('get correct gas data', async () => { + await expect(arbGasDataProvider.getGasData()).resolves.toMatchObject({ + perArbGasTotal: BigNumber.from('0x05f5e100'), + perL1CalldataFee: BigNumber.from('0x072ac022fb'), + perL2TxFee: BigNumber.from('0x3eb611321440'), + }); + }); +}); diff --git a/test/unit/routers/alpha-router/util/gas-factory-helpers.test.ts b/test/unit/routers/alpha-router/util/gas-factory-helpers.test.ts index 34cc6e9ba..0cc72f9f4 100644 --- a/test/unit/routers/alpha-router/util/gas-factory-helpers.test.ts +++ b/test/unit/routers/alpha-router/util/gas-factory-helpers.test.ts @@ -30,6 +30,7 @@ import { TradeType } from '@uniswap/sdk-core'; import { Trade } from '@uniswap/router-sdk'; import { Route } from '@uniswap/v3-sdk'; import { getPools } from '../gas-models/test-util/helpers'; +import { ArbitrumGasData } from '../../../../../src/providers/v3/gas-data-provider'; const mockUSDCNativePools = [ USDC_WETH_LOW_LIQ_LOW, @@ -120,7 +121,7 @@ describe('gas factory helpers tests', () => { mockPoolProvider, providerConfig, gasToken - ); + ); const v3GasModel = await (new V3HeuristicGasModelFactory()).buildGasModel({ chainId: chainId, @@ -189,6 +190,25 @@ describe('gas factory helpers tests', () => { expect(estimatedGasUsedGasToken?.currency.equals(gasToken)).toBe(true); expect(estimatedGasUsedGasToken?.toExact()).not.toEqual('0'); expect(quoteGasAdjusted.lessThan(mockSwapRoute.quote)).toBe(true); + + const arbGasData: ArbitrumGasData = { + perL2TxFee: BigNumber.from(1_000_000), + perL1CalldataFee: BigNumber.from(1_000), + perArbGasTotal: BigNumber.from(1_000_000_000), + } + + const { + estimatedGasUsedQuoteToken: estimatedGasUsedQuoteTokenArb, + estimatedGasUsedUSD: estimatedGasUsedUSDArb, + estimatedGasUsedGasToken: estimatedGasUsedGasTokenArb, + quoteGasAdjusted: quoteGasAdjustedArb + } = await calculateGasUsed(chainId, mockSwapRoute, simulatedGasUsed, getMockedV2PoolProvider(), mockPoolProvider, arbGasData, providerConfig); + + // Arbitrum gas data should not affect the quote gas or USD amounts + expect(estimatedGasUsedQuoteTokenArb.currency.equals(quoteToken)).toBe(true); + expect(estimatedGasUsedUSDArb.equalTo(estimatedGasUsedUSD)).toBe(true); + expect(estimatedGasUsedGasTokenArb?.currency.equals(gasToken)).toBe(true); + expect(quoteGasAdjustedArb.equalTo(quoteGasAdjusted)).toBe(true); }) }) });