@hyperlane-xyz/core
Version:
Core solidity contracts for Hyperlane
199 lines (162 loc) • 6.37 kB
text/typescript
/* Imports: External */
import { BigNumber, utils } from 'ethers'
import { serialize } from '@ethersproject/transactions'
import { predeploys, getContractFactory } from '@eth-optimism/contracts'
/* Imports: Internal */
import { expect } from './shared/setup'
import { hardhatTest, gasPriceOracleWallet } from './shared/utils'
import { OptimismEnv } from './shared/env'
const setPrices = async (env: OptimismEnv, value: number | BigNumber) => {
const gasPrice = await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).setGasPrice(value)
await gasPrice.wait()
const baseFee = await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).setL1BaseFee(value)
await baseFee.wait()
}
describe('Fee Payment Integration Tests', async () => {
let env: OptimismEnv
const other = '0x1234123412341234123412341234123412341234'
before(async () => {
env = await OptimismEnv.new()
})
hardhatTest(
`should return eth_gasPrice equal to OVM_GasPriceOracle.gasPrice`,
async () => {
const assertGasPrice = async () => {
const gasPrice = await env.l2Wallet.getGasPrice()
const oracleGasPrice =
await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).gasPrice()
expect(gasPrice).to.deep.equal(oracleGasPrice)
}
await assertGasPrice()
// update the gas price
const tx = await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).setGasPrice(1000)
await tx.wait()
await assertGasPrice()
}
)
hardhatTest('Paying a nonzero but acceptable gasPrice fee', async () => {
await setPrices(env, 1000)
const amount = utils.parseEther('0.0000001')
const balanceBefore = await env.l2Wallet.getBalance()
const feeVaultBalanceBefore = await env.l2Wallet.provider.getBalance(
env.messenger.contracts.l2.OVM_SequencerFeeVault.address
)
expect(balanceBefore.gt(amount))
const unsigned = await env.l2Wallet.populateTransaction({
to: other,
value: amount,
gasLimit: 500000,
})
const raw = serialize({
nonce: parseInt(unsigned.nonce.toString(10), 10),
value: unsigned.value,
gasPrice: unsigned.gasPrice,
gasLimit: unsigned.gasLimit,
to: unsigned.to,
data: unsigned.data,
})
const l1Fee = await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).getL1Fee(raw)
const tx = await env.l2Wallet.sendTransaction(unsigned)
const receipt = await tx.wait()
expect(receipt.status).to.eq(1)
const balanceAfter = await env.l2Wallet.getBalance()
const feeVaultBalanceAfter = await env.l2Wallet.provider.getBalance(
env.messenger.contracts.l2.OVM_SequencerFeeVault.address
)
const l2Fee = receipt.gasUsed.mul(tx.gasPrice)
const expectedFeePaid = l1Fee.add(l2Fee)
expect(balanceBefore.sub(balanceAfter)).to.deep.equal(
expectedFeePaid.add(amount)
)
// Make sure the fee was transferred to the vault.
expect(feeVaultBalanceAfter.sub(feeVaultBalanceBefore)).to.deep.equal(
expectedFeePaid
)
await setPrices(env, 1)
})
hardhatTest('should compute correct fee', async () => {
await setPrices(env, 1000)
const preBalance = await env.l2Wallet.getBalance()
const OVM_GasPriceOracle = getContractFactory('OVM_GasPriceOracle')
.attach(predeploys.OVM_GasPriceOracle)
.connect(env.l2Wallet)
const WETH = getContractFactory('OVM_ETH')
.attach(predeploys.OVM_ETH)
.connect(env.l2Wallet)
const feeVaultBefore = await WETH.balanceOf(
predeploys.OVM_SequencerFeeVault
)
const unsigned = await env.l2Wallet.populateTransaction({
to: env.l2Wallet.address,
value: 0,
})
const raw = serialize({
nonce: parseInt(unsigned.nonce.toString(10), 10),
value: unsigned.value,
gasPrice: unsigned.gasPrice,
gasLimit: unsigned.gasLimit,
to: unsigned.to,
data: unsigned.data,
})
const l1Fee = await OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).getL1Fee(raw)
const tx = await env.l2Wallet.sendTransaction(unsigned)
const receipt = await tx.wait()
const l2Fee = receipt.gasUsed.mul(tx.gasPrice)
const postBalance = await env.l2Wallet.getBalance()
const feeVaultAfter = await WETH.balanceOf(predeploys.OVM_SequencerFeeVault)
const fee = l1Fee.add(l2Fee)
const balanceDiff = preBalance.sub(postBalance)
const feeReceived = feeVaultAfter.sub(feeVaultBefore)
expect(balanceDiff).to.deep.equal(fee)
// There is no inflation
expect(feeReceived).to.deep.equal(balanceDiff)
await setPrices(env, 1)
})
it('should not be able to withdraw fees before the minimum is met', async () => {
await expect(env.messenger.contracts.l2.OVM_SequencerFeeVault.withdraw()).to
.be.rejected
})
hardhatTest(
'should be able to withdraw fees back to L1 once the minimum is met',
async () => {
const l1FeeWallet =
await env.messenger.contracts.l2.OVM_SequencerFeeVault.l1FeeWallet()
const balanceBefore = await env.l1Wallet.provider.getBalance(l1FeeWallet)
const withdrawalAmount =
await env.messenger.contracts.l2.OVM_SequencerFeeVault.MIN_WITHDRAWAL_AMOUNT()
// Transfer the minimum required to withdraw.
const tx = await env.l2Wallet.sendTransaction({
to: env.messenger.contracts.l2.OVM_SequencerFeeVault.address,
value: withdrawalAmount,
gasLimit: 500000,
})
await tx.wait()
const vaultBalance = await env.messenger.contracts.l2.OVM_ETH.balanceOf(
env.messenger.contracts.l2.OVM_SequencerFeeVault.address
)
const withdrawTx =
await env.messenger.contracts.l2.OVM_SequencerFeeVault.withdraw()
// Wait for the withdrawal to be relayed to L1.
await withdrawTx.wait()
await env.relayXDomainMessages(withdrawTx)
await env.waitForXDomainTransaction(withdrawTx)
// Balance difference should be equal to old L2 balance.
const balanceAfter = await env.l1Wallet.provider.getBalance(l1FeeWallet)
expect(balanceAfter.sub(balanceBefore)).to.deep.equal(
BigNumber.from(vaultBalance)
)
}
)
})