Commit a8291f10 authored by smartcontracts's avatar smartcontracts Committed by GitHub

maint(ct): clean up L1StandardBridge tests (#2458)

Cleans up tests for the L1StandardBridge. Removes a lot of unnecessary
code and simplifies certain tests by bundling things into a single
assertion.
Co-authored-by: default avatarmergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
parent 2925b1d2
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract, constants } from 'ethers'
import { Interface } from 'ethers/lib/utils'
import {
smock,
MockContractFactory,
FakeContract,
MockContract,
} from '@defi-wonderland/smock'
/* Internal Imports */
import { Signer, Contract, constants } from 'ethers'
import { smock, FakeContract, MockContract } from '@defi-wonderland/smock'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import { expect } from '../../../setup'
import { NON_NULL_BYTES32, NON_ZERO_ADDRESS } from '../../../helpers'
import { NON_NULL_BYTES32, NON_ZERO_ADDRESS, deploy } from '../../../helpers'
import { getContractInterface, predeploys } from '../../../../src'
const ERR_INVALID_MESSENGER = 'OVM_XCHAIN: messenger contract unauthenticated'
const ERR_INVALID_X_DOMAIN_MSG_SENDER =
'OVM_XCHAIN: wrong sender of cross-domain message'
const ERR_ALREADY_INITIALIZED = 'Contract has already been initialized.'
const DUMMY_L2_ERC20_ADDRESS = ethers.utils.getAddress('0x' + 'abba'.repeat(10))
const DUMMY_L2_BRIDGE_ADDRESS = ethers.utils.getAddress(
'0x' + 'acdc'.repeat(10)
)
// TODO: Maybe we should consider automatically generating these and exporting them?
const ERROR_STRINGS = {
INVALID_MESSENGER: 'OVM_XCHAIN: messenger contract unauthenticated',
INVALID_X_DOMAIN_MSG_SENDER:
'OVM_XCHAIN: wrong sender of cross-domain message',
ALREADY_INITIALIZED: 'Contract has already been initialized.',
}
const DUMMY_L2_ERC20_ADDRESS = '0xaBBAABbaaBbAABbaABbAABbAABbaAbbaaBbaaBBa'
const DUMMY_L2_BRIDGE_ADDRESS = '0xACDCacDcACdCaCDcacdcacdCaCdcACdCAcDcaCdc'
const INITIAL_TOTAL_L1_SUPPLY = 5000
const FINALIZATION_GAS = 1_200_000
describe('L1StandardBridge', () => {
// init signers
let l1MessengerImpersonator: Signer
let alice: Signer
let bob: Signer
let bobsAddress
let aliceAddress
// we can just make up this string since it's on the "other" Layer
let Factory__L1ERC20: MockContractFactory<ContractFactory>
let IL2ERC20Bridge: Interface
let alice: SignerWithAddress
let bob: SignerWithAddress
before(async () => {
;[l1MessengerImpersonator, alice, bob] = await ethers.getSigners()
await smock.fake<Contract>('OVM_ETH')
// deploy an ERC20 contract on L1
Factory__L1ERC20 = await smock.mock(
'@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20'
)
// get an L2ER20Bridge Interface
IL2ERC20Bridge = getContractInterface('IL2ERC20Bridge')
aliceAddress = await alice.getAddress()
bobsAddress = await bob.getAddress()
})
let L1ERC20: MockContract<Contract>
......@@ -65,19 +39,18 @@ describe('L1StandardBridge', () => {
)
// Deploy the contract under test
L1StandardBridge = await (
await ethers.getContractFactory('L1StandardBridge')
).deploy()
L1StandardBridge = await deploy('L1StandardBridge')
await L1StandardBridge.initialize(
Fake__L1CrossDomainMessenger.address,
DUMMY_L2_BRIDGE_ADDRESS
)
L1ERC20 = await Factory__L1ERC20.deploy('L1ERC20', 'ERC')
L1ERC20 = await (
await smock.mock('@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20')
).deploy('L1ERC20', 'ERC')
await L1ERC20.setVariable('_totalSupply', INITIAL_TOTAL_L1_SUPPLY)
await L1ERC20.setVariable('_balances', {
[aliceAddress]: INITIAL_TOTAL_L1_SUPPLY,
[alice.address]: INITIAL_TOTAL_L1_SUPPLY,
})
})
......@@ -88,7 +61,7 @@ describe('L1StandardBridge', () => {
ethers.constants.AddressZero,
DUMMY_L2_BRIDGE_ADDRESS
)
).to.be.revertedWith(ERR_ALREADY_INITIALIZED)
).to.be.revertedWith(ERROR_STRINGS.ALREADY_INITIALIZED)
})
})
......@@ -107,8 +80,7 @@ describe('L1StandardBridge', () => {
const depositAmount = 1_000
it('depositETH() escrows the deposit amount and sends the correct deposit message', async () => {
const depositer = await alice.getAddress()
const initialBalance = await ethers.provider.getBalance(depositer)
const initialBalance = await alice.getBalance()
// alice calls deposit on the bridge and the L1 bridge calls transferFrom on the token
const res = await L1StandardBridge.connect(alice).depositETH(
......@@ -119,92 +91,80 @@ describe('L1StandardBridge', () => {
}
)
const depositCallToMessenger =
Fake__L1CrossDomainMessenger.sendMessage.getCall(0)
expect(
Fake__L1CrossDomainMessenger.sendMessage.getCall(0).args
).to.deep.equal([
DUMMY_L2_BRIDGE_ADDRESS,
getContractInterface('IL2ERC20Bridge').encodeFunctionData(
'finalizeDeposit',
[
constants.AddressZero,
predeploys.OVM_ETH,
alice.address,
alice.address,
depositAmount,
NON_NULL_BYTES32,
]
),
FINALIZATION_GAS,
])
const depositerBalance = await ethers.provider.getBalance(depositer)
const receipt = await res.wait()
const depositerFeePaid = receipt.cumulativeGasUsed.mul(
receipt.effectiveGasPrice
)
expect(depositerBalance).to.equal(
expect(await alice.getBalance()).to.equal(
initialBalance.sub(depositAmount).sub(depositerFeePaid)
)
// bridge's balance is increased
const bridgeBalance = await ethers.provider.getBalance(
L1StandardBridge.address
)
expect(bridgeBalance).to.equal(depositAmount)
// Check the correct cross-chain call was sent:
// Message should be sent to the L2 bridge
expect(depositCallToMessenger.args[0]).to.equal(DUMMY_L2_BRIDGE_ADDRESS)
// Message data should be a call telling the L2ETHToken to finalize the deposit
// the L1 bridge sends the correct message to the L1 messenger
expect(depositCallToMessenger.args[1]).to.equal(
IL2ERC20Bridge.encodeFunctionData('finalizeDeposit', [
constants.AddressZero,
predeploys.OVM_ETH,
depositer,
depositer,
depositAmount,
NON_NULL_BYTES32,
])
)
expect(depositCallToMessenger.args[2]).to.equal(FINALIZATION_GAS)
expect(
await ethers.provider.getBalance(L1StandardBridge.address)
).to.equal(depositAmount)
})
it('depositETHTo() escrows the deposit amount and sends the correct deposit message', async () => {
// depositor calls deposit on the bridge and the L1 bridge calls transferFrom on the token
const initialBalance = await ethers.provider.getBalance(aliceAddress)
const initialBalance = await alice.getBalance()
const res = await L1StandardBridge.connect(alice).depositETHTo(
bobsAddress,
bob.address,
FINALIZATION_GAS,
NON_NULL_BYTES32,
{
value: depositAmount,
}
)
const depositCallToMessenger =
Fake__L1CrossDomainMessenger.sendMessage.getCall(0)
const depositerBalance = await ethers.provider.getBalance(aliceAddress)
expect(
Fake__L1CrossDomainMessenger.sendMessage.getCall(0).args
).to.deep.equal([
DUMMY_L2_BRIDGE_ADDRESS,
getContractInterface('IL2ERC20Bridge').encodeFunctionData(
'finalizeDeposit',
[
constants.AddressZero,
predeploys.OVM_ETH,
alice.address,
bob.address,
depositAmount,
NON_NULL_BYTES32,
]
),
FINALIZATION_GAS,
])
const receipt = await res.wait()
const depositerFeePaid = receipt.cumulativeGasUsed.mul(
receipt.effectiveGasPrice
)
expect(depositerBalance).to.equal(
expect(await alice.getBalance()).to.equal(
initialBalance.sub(depositAmount).sub(depositerFeePaid)
)
// bridge's balance is increased
const bridgeBalance = await ethers.provider.getBalance(
L1StandardBridge.address
)
expect(bridgeBalance).to.equal(depositAmount)
// Check the correct cross-chain call was sent:
// Message should be sent to the L2 bridge
expect(depositCallToMessenger.args[0]).to.equal(DUMMY_L2_BRIDGE_ADDRESS)
// Message data should be a call telling the L2ETHToken to finalize the deposit
// the L1 bridge sends the correct message to the L1 messenger
expect(depositCallToMessenger.args[1]).to.equal(
IL2ERC20Bridge.encodeFunctionData('finalizeDeposit', [
constants.AddressZero,
predeploys.OVM_ETH,
aliceAddress,
bobsAddress,
depositAmount,
NON_NULL_BYTES32,
])
)
expect(depositCallToMessenger.args[2]).to.equal(FINALIZATION_GAS)
expect(
await ethers.provider.getBalance(L1StandardBridge.address)
).to.equal(depositAmount)
})
it('cannot depositETH from a contract account', async () => {
......@@ -218,29 +178,17 @@ describe('L1StandardBridge', () => {
describe('ETH withdrawals', () => {
it('onlyFromCrossDomainAccount: should revert on calls from a non-crossDomainMessenger L1 account', async () => {
// Deploy new bridge, initialize with random messenger
await expect(
L1StandardBridge.connect(alice).finalizeETHWithdrawal(
constants.AddressZero,
constants.AddressZero,
1,
NON_NULL_BYTES32,
{
from: aliceAddress,
}
NON_NULL_BYTES32
)
).to.be.revertedWith(ERR_INVALID_MESSENGER)
).to.be.revertedWith(ERROR_STRINGS.INVALID_MESSENGER)
})
it('onlyFromCrossDomainAccount: should revert on calls from the right crossDomainMessenger, but wrong xDomainMessageSender (ie. not the L2ETHToken)', async () => {
L1StandardBridge = await (
await ethers.getContractFactory('L1StandardBridge')
).deploy()
await L1StandardBridge.initialize(
Fake__L1CrossDomainMessenger.address,
DUMMY_L2_BRIDGE_ADDRESS
)
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
'0x' + '22'.repeat(20)
)
......@@ -255,23 +203,21 @@ describe('L1StandardBridge', () => {
from: Fake__L1CrossDomainMessenger.address,
}
)
).to.be.revertedWith(ERR_INVALID_X_DOMAIN_MSG_SENDER)
).to.be.revertedWith(ERROR_STRINGS.INVALID_X_DOMAIN_MSG_SENDER)
})
it('should revert in nothing to withdraw', async () => {
// make sure no balance at start of test
expect(await ethers.provider.getBalance(NON_ZERO_ADDRESS)).to.be.equal(0)
const withdrawalAmount = 100
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
() => DUMMY_L2_BRIDGE_ADDRESS
DUMMY_L2_BRIDGE_ADDRESS
)
await expect(
L1StandardBridge.finalizeETHWithdrawal(
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
withdrawalAmount,
100,
NON_NULL_BYTES32,
{
from: Fake__L1CrossDomainMessenger.address,
......@@ -283,15 +229,13 @@ describe('L1StandardBridge', () => {
})
it('should credit funds to the withdrawer and not use too much gas', async () => {
// make sure no balance at start of test
expect(await ethers.provider.getBalance(NON_ZERO_ADDRESS)).to.be.equal(0)
const withdrawalAmount = 100
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
() => DUMMY_L2_BRIDGE_ADDRESS
DUMMY_L2_BRIDGE_ADDRESS
)
// thanks Alice
await L1StandardBridge.connect(alice).depositETH(
FINALIZATION_GAS,
NON_NULL_BYTES32,
......@@ -327,7 +271,6 @@ describe('L1StandardBridge', () => {
})
it('depositERC20() escrows the deposit amount and sends the correct deposit message', async () => {
// alice calls deposit on the bridge and the L1 bridge calls transferFrom on the token
await L1StandardBridge.connect(alice).depositERC20(
L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
......@@ -336,73 +279,68 @@ describe('L1StandardBridge', () => {
NON_NULL_BYTES32
)
const depositCallToMessenger =
Fake__L1CrossDomainMessenger.sendMessage.getCall(0)
const depositerBalance = await L1ERC20.balanceOf(aliceAddress)
expect(depositerBalance).to.equal(INITIAL_TOTAL_L1_SUPPLY - depositAmount)
// bridge's balance is increased
const bridgeBalance = await L1ERC20.balanceOf(L1StandardBridge.address)
expect(bridgeBalance).to.equal(depositAmount)
expect(
Fake__L1CrossDomainMessenger.sendMessage.getCall(0).args
).to.deep.equal([
DUMMY_L2_BRIDGE_ADDRESS,
getContractInterface('IL2ERC20Bridge').encodeFunctionData(
'finalizeDeposit',
[
L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
alice.address,
alice.address,
depositAmount,
NON_NULL_BYTES32,
]
),
FINALIZATION_GAS,
])
// Check the correct cross-chain call was sent:
// Message should be sent to the L2 bridge
expect(depositCallToMessenger.args[0]).to.equal(DUMMY_L2_BRIDGE_ADDRESS)
// Message data should be a call telling the L2DepositedERC20 to finalize the deposit
expect(await L1ERC20.balanceOf(alice.address)).to.equal(
INITIAL_TOTAL_L1_SUPPLY - depositAmount
)
// the L1 bridge sends the correct message to the L1 messenger
expect(depositCallToMessenger.args[1]).to.equal(
IL2ERC20Bridge.encodeFunctionData('finalizeDeposit', [
L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
aliceAddress,
aliceAddress,
depositAmount,
NON_NULL_BYTES32,
])
expect(await L1ERC20.balanceOf(L1StandardBridge.address)).to.equal(
depositAmount
)
expect(depositCallToMessenger.args[2]).to.equal(FINALIZATION_GAS)
})
it('depositERC20To() escrows the deposit amount and sends the correct deposit message', async () => {
// depositor calls deposit on the bridge and the L1 bridge calls transferFrom on the token
await L1StandardBridge.connect(alice).depositERC20To(
L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
bobsAddress,
bob.address,
depositAmount,
FINALIZATION_GAS,
NON_NULL_BYTES32
)
const depositCallToMessenger =
Fake__L1CrossDomainMessenger.sendMessage.getCall(0)
const depositerBalance = await L1ERC20.balanceOf(aliceAddress)
expect(depositerBalance).to.equal(INITIAL_TOTAL_L1_SUPPLY - depositAmount)
// bridge's balance is increased
const bridgeBalance = await L1ERC20.balanceOf(L1StandardBridge.address)
expect(bridgeBalance).to.equal(depositAmount)
expect(
Fake__L1CrossDomainMessenger.sendMessage.getCall(0).args
).to.deep.equal([
DUMMY_L2_BRIDGE_ADDRESS,
getContractInterface('IL2ERC20Bridge').encodeFunctionData(
'finalizeDeposit',
[
L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
alice.address,
bob.address,
depositAmount,
NON_NULL_BYTES32,
]
),
FINALIZATION_GAS,
])
// Check the correct cross-chain call was sent:
// Message should be sent to the L2DepositedERC20 on L2
expect(depositCallToMessenger.args[0]).to.equal(DUMMY_L2_BRIDGE_ADDRESS)
// Message data should be a call telling the L2DepositedERC20 to finalize the deposit
expect(await L1ERC20.balanceOf(alice.address)).to.equal(
INITIAL_TOTAL_L1_SUPPLY - depositAmount
)
// the L1 bridge sends the correct message to the L1 messenger
expect(depositCallToMessenger.args[1]).to.equal(
IL2ERC20Bridge.encodeFunctionData('finalizeDeposit', [
L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
aliceAddress,
bobsAddress,
depositAmount,
NON_NULL_BYTES32,
])
expect(await L1ERC20.balanceOf(L1StandardBridge.address)).to.equal(
depositAmount
)
expect(depositCallToMessenger.args[2]).to.equal(FINALIZATION_GAS)
})
it('cannot depositERC20 from a contract account', async () => {
......@@ -419,12 +357,8 @@ describe('L1StandardBridge', () => {
describe('Handling ERC20.transferFrom() failures that revert ', () => {
let Fake__L1ERC20: FakeContract
before(async () => {
// Deploy the L1 ERC20 token, Alice will receive the full initialSupply
Fake__L1ERC20 = await smock.fake<Contract>(
await Factory__L1ERC20.deploy('L1ERC20', 'ERC')
)
Fake__L1ERC20 = await smock.fake<Contract>('ERC20')
Fake__L1ERC20.transferFrom.reverts()
})
......@@ -445,7 +379,7 @@ describe('L1StandardBridge', () => {
L1StandardBridge.connect(alice).depositERC20To(
Fake__L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
bobsAddress,
bob.address,
depositAmount,
FINALIZATION_GAS,
NON_NULL_BYTES32
......@@ -458,7 +392,7 @@ describe('L1StandardBridge', () => {
L1StandardBridge.connect(alice).depositERC20To(
ethers.constants.AddressZero,
DUMMY_L2_ERC20_ADDRESS,
bobsAddress,
bob.address,
depositAmount,
FINALIZATION_GAS,
NON_NULL_BYTES32
......@@ -470,9 +404,7 @@ describe('L1StandardBridge', () => {
describe('Handling ERC20.transferFrom failures that return false', () => {
let Fake__L1ERC20: FakeContract
before(async () => {
Fake__L1ERC20 = await smock.fake(
await Factory__L1ERC20.deploy('L1ERC20', 'ERC')
)
Fake__L1ERC20 = await smock.fake('ERC20')
Fake__L1ERC20.transferFrom.returns(false)
})
......@@ -493,7 +425,7 @@ describe('L1StandardBridge', () => {
L1StandardBridge.depositERC20To(
Fake__L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
bobsAddress,
bob.address,
depositAmount,
FINALIZATION_GAS,
NON_NULL_BYTES32
......@@ -514,12 +446,12 @@ describe('L1StandardBridge', () => {
1,
NON_NULL_BYTES32
)
).to.be.revertedWith(ERR_INVALID_MESSENGER)
).to.be.revertedWith(ERROR_STRINGS.INVALID_MESSENGER)
})
it('onlyFromCrossDomainAccount: should revert on calls from the right crossDomainMessenger, but wrong xDomainMessageSender (ie. not the L2DepositedERC20)', async () => {
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
() => NON_ZERO_ADDRESS
NON_ZERO_ADDRESS
)
await expect(
......@@ -534,7 +466,7 @@ describe('L1StandardBridge', () => {
from: Fake__L1CrossDomainMessenger.address,
}
)
).to.be.revertedWith(ERR_INVALID_X_DOMAIN_MSG_SENDER)
).to.be.revertedWith(ERROR_STRINGS.INVALID_X_DOMAIN_MSG_SENDER)
})
it('should credit funds to the withdrawer and not use too much gas', async () => {
......@@ -561,7 +493,7 @@ describe('L1StandardBridge', () => {
expect(await L1ERC20.balanceOf(NON_ZERO_ADDRESS)).to.be.equal(0)
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
() => DUMMY_L2_BRIDGE_ADDRESS
DUMMY_L2_BRIDGE_ADDRESS
)
await L1StandardBridge.finalizeERC20Withdrawal(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment