diff --git a/packages/contracts/contracts/test-libraries/utils/TestLib_Buffer.sol b/packages/contracts/contracts/test-libraries/utils/TestLib_Buffer.sol index 79a65b29d46ef..e727ce2cce3a4 100644 --- a/packages/contracts/contracts/test-libraries/utils/TestLib_Buffer.sol +++ b/packages/contracts/contracts/test-libraries/utils/TestLib_Buffer.sol @@ -9,6 +9,7 @@ import { Lib_Buffer } from "../../libraries/utils/Lib_Buffer.sol"; */ contract TestLib_Buffer { using Lib_Buffer for Lib_Buffer.Buffer; + using Lib_Buffer for Lib_Buffer.BufferContext; Lib_Buffer.Buffer internal buf; @@ -16,6 +17,10 @@ contract TestLib_Buffer { buf.push(_value, _extraData); } + function push(bytes32 _value) public { + buf.push(_value); + } + function get(uint256 _index) public view returns (bytes32) { return buf.get(_index); } @@ -39,4 +44,16 @@ contract TestLib_Buffer { function getExtraData() public view returns (bytes27) { return buf.getExtraData(); } + + function getContext() public view returns (Lib_Buffer.BufferContext memory) { + return buf.getContext(); + } + + function setContext(uint40 _index, bytes27 _extraData) public { + Lib_Buffer.BufferContext memory _ctx = Lib_Buffer.BufferContext({ + length: _index, + extraData: _extraData + }); + return buf.setContext(_ctx); + } } diff --git a/packages/contracts/test/contracts/L1/deployment/AddressDictator.spec.ts b/packages/contracts/test/contracts/L1/deployment/AddressDictator.spec.ts new file mode 100644 index 0000000000000..91bf86c1508f5 --- /dev/null +++ b/packages/contracts/test/contracts/L1/deployment/AddressDictator.spec.ts @@ -0,0 +1,90 @@ +/* External Imports */ +import { ethers } from 'hardhat' +import { Contract, Signer, ContractFactory } from 'ethers' + +/* Internal Imports */ +import { expect } from '../../../setup' +import { NON_ZERO_ADDRESS } from '../../../helpers' + +describe('AddressDictator', () => { + let signer: Signer + let otherSigner: Signer + let signerAddress: string + let Factory__AddressDictator: ContractFactory + let Factory__Lib_AddressManager: ContractFactory + before(async () => { + ;[signer, otherSigner] = await ethers.getSigners() + + Factory__AddressDictator = await ethers.getContractFactory( + 'AddressDictator' + ) + + Factory__Lib_AddressManager = await ethers.getContractFactory( + 'Lib_AddressManager' + ) + + signerAddress = await signer.getAddress() + }) + + let AddressDictator: Contract + let Lib_AddressManager: Contract + beforeEach(async () => { + Lib_AddressManager = await Factory__Lib_AddressManager.connect( + signer + ).deploy() + + AddressDictator = await Factory__AddressDictator.connect(signer).deploy( + Lib_AddressManager.address, + signerAddress, + ['addr1'], + [NON_ZERO_ADDRESS] + ) + + Lib_AddressManager.transferOwnership(AddressDictator.address) + }) + + describe('initialize', () => { + it('should revert when providing wrong arguments', async () => { + await expect( + Factory__AddressDictator.connect(signer).deploy( + Lib_AddressManager.address, + signerAddress, + ['addr1', 'addr2'], + [NON_ZERO_ADDRESS] + ) + ).to.be.revertedWith( + 'AddressDictator: Must provide an equal number of names and addresses.' + ) + }) + }) + + describe('setAddresses', async () => { + it('should change the addresses associated with a name', async () => { + await AddressDictator.setAddresses() + expect(await Lib_AddressManager.getAddress('addr1')).to.be.equal( + NON_ZERO_ADDRESS + ) + }) + }) + + describe('getNamedAddresses', () => { + it('should return all the addresses and their names', async () => { + expect(await AddressDictator.getNamedAddresses()).to.be.deep.equal([ + ['addr1', NON_ZERO_ADDRESS], + ]) + }) + }) + + describe('returnOwnership', () => { + it('should transfer contract ownership to finalOwner', async () => { + await expect(AddressDictator.connect(signer).returnOwnership()).to.not.be + .reverted + }) + + it('should revert when called by non-owner', async () => { + await expect( + AddressDictator.connect(otherSigner).returnOwnership() + ).to.be.revertedWith('AddressDictator: only callable by finalOwner') + }) + }) +}) diff --git a/packages/contracts/test/contracts/L1/deployment/ChugSplashDictator.spec..ts b/packages/contracts/test/contracts/L1/deployment/ChugSplashDictator.spec..ts new file mode 100644 index 0000000000000..e6767fd48a17d --- /dev/null +++ b/packages/contracts/test/contracts/L1/deployment/ChugSplashDictator.spec..ts @@ -0,0 +1,76 @@ +/* External Imports */ +import { ethers } from 'hardhat' +import { Contract, Signer, ContractFactory } from 'ethers' + +/* Internal Imports */ +import { expect } from '../../../setup' + +describe('ChugSplashDictator', () => { + let signer: Signer + let otherSigner: Signer + let signerAddress: string + + let Factory__L1ChugSplashProxy: ContractFactory + let Factory__ChugSplashDictator: ContractFactory + before(async () => { + ;[signer, otherSigner] = await ethers.getSigners() + + Factory__L1ChugSplashProxy = await ethers.getContractFactory( + 'L1ChugSplashProxy' + ) + + Factory__ChugSplashDictator = await ethers.getContractFactory( + 'ChugSplashDictator' + ) + + signerAddress = await signer.getAddress() + }) + + let L1ChugSplashProxy: Contract + let ChugSplashDictator: Contract + beforeEach(async () => { + L1ChugSplashProxy = await Factory__L1ChugSplashProxy.connect(signer).deploy( + signerAddress + ) + + ChugSplashDictator = await Factory__ChugSplashDictator.connect( + signer + ).deploy( + L1ChugSplashProxy.address, + signerAddress, + ethers.utils.keccak256('0x1111'), + ethers.utils.keccak256('0x1234'), + ethers.utils.keccak256('0x5678'), + ethers.utils.keccak256('0x1234'), + ethers.utils.keccak256('0x1234') + ) + + await L1ChugSplashProxy.connect(signer).setOwner(ChugSplashDictator.address) + }) + + describe('doActions', () => { + it('should revert when sent wrong code', async () => { + await expect(ChugSplashDictator.doActions('0x2222')).to.be.revertedWith( + 'ChugSplashDictator: Incorrect code hash.' + ) + }) + + it('should set the proxy code, storage & owner', async () => { + await expect(ChugSplashDictator.connect(signer).doActions('0x1111')).to + .not.be.reverted + }) + }) + + describe('returnOwnership', () => { + it('should transfer contractc ownership to finalOwner', async () => { + await expect(ChugSplashDictator.connect(signer).returnOwnership()).to.not + .be.reverted + }) + + it('should revert when called by non-owner', async () => { + await expect( + ChugSplashDictator.connect(otherSigner).returnOwnership() + ).to.be.revertedWith('ChugSplashDictator: only callable by finalOwner') + }) + }) +}) diff --git a/packages/contracts/test/contracts/L1/messaging/L1CrossDomainMessenger.spec.ts b/packages/contracts/test/contracts/L1/messaging/L1CrossDomainMessenger.spec.ts index bd2f7f37da598..e4defd8a78ae5 100644 --- a/packages/contracts/test/contracts/L1/messaging/L1CrossDomainMessenger.spec.ts +++ b/packages/contracts/test/contracts/L1/messaging/L1CrossDomainMessenger.spec.ts @@ -2,6 +2,7 @@ import { ethers } from 'hardhat' import { Signer, ContractFactory, Contract, BigNumber } from 'ethers' import { smockit, MockContract } from '@eth-optimism/smock' +import { smock, MockContractFactory } from '@defi-wonderland/smock' import { remove0x, toHexString, @@ -347,6 +348,28 @@ describe('L1CrossDomainMessenger', () => { }) }) + describe('xDomainMessageSender', () => { + let Mock__Factory__L1CrossDomainMessenger: MockContractFactory + let Mock__L1CrossDomainMessenger + before(async () => { + Mock__Factory__L1CrossDomainMessenger = await smock.mock( + 'L1CrossDomainMessenger' + ) + Mock__L1CrossDomainMessenger = + await Mock__Factory__L1CrossDomainMessenger.deploy() + }) + + it('should return the xDomainMsgSender address', async () => { + await Mock__L1CrossDomainMessenger.setVariable( + 'xDomainMsgSender', + '0x0000000000000000000000000000000000000000' + ) + expect( + await Mock__L1CrossDomainMessenger.xDomainMessageSender() + ).to.equal('0x0000000000000000000000000000000000000000') + }) + }) + const generateMockRelayMessageProof = async ( target: string, sender: string, diff --git a/packages/contracts/test/contracts/L1/messaging/L1StandardBridge.spec.ts b/packages/contracts/test/contracts/L1/messaging/L1StandardBridge.spec.ts index 7f26c146c7615..e5ecb52146aee 100644 --- a/packages/contracts/test/contracts/L1/messaging/L1StandardBridge.spec.ts +++ b/packages/contracts/test/contracts/L1/messaging/L1StandardBridge.spec.ts @@ -88,6 +88,17 @@ describe('L1StandardBridge', () => { }) }) + describe('receive', () => { + it('should send an amount of ETH to the callers balance on L2', async () => { + await expect( + alice.sendTransaction({ + to: L1StandardBridge.address, + data: '0x', + }) + ).to.not.be.reverted + }) + }) + describe('ETH deposits', () => { const depositAmount = 1_000 @@ -564,4 +575,18 @@ describe('L1StandardBridge', () => { ) }) }) + + describe('donateETH', () => { + it('it should just call the function', async () => { + await expect(L1StandardBridge.donateETH()).to.not.be.reverted + }) + + it('should send ETH to the contract account', async () => { + await expect( + L1StandardBridge.donateETH({ + value: 100, + }) + ).to.not.be.reverted + }) + }) }) diff --git a/packages/contracts/test/contracts/L1/rollup/CanonicalTransactionChain.spec.ts b/packages/contracts/test/contracts/L1/rollup/CanonicalTransactionChain.spec.ts index 9742adde39416..023418f9ac741 100644 --- a/packages/contracts/test/contracts/L1/rollup/CanonicalTransactionChain.spec.ts +++ b/packages/contracts/test/contracts/L1/rollup/CanonicalTransactionChain.spec.ts @@ -218,6 +218,18 @@ describe('CanonicalTransactionChain', () => { ).to.be.revertedWith('Insufficient gas for L2 rate limiting burn.') }) + it('should burn L1 gas when L2 gas limit is high', async () => { + const _enqueueL2GasPrepaid = + await CanonicalTransactionChain.enqueueL2GasPrepaid() + const data = '0x' + '12'.repeat(1234) + + // Create a tx with high L2 gas limit + const l2GasLimit = 4 * _enqueueL2GasPrepaid + + await expect(CanonicalTransactionChain.enqueue(target, l2GasLimit, data)) + .to.not.be.reverted + }) + describe('with valid input parameters', () => { it('should emit a TransactionEnqueued event', async () => { const timestamp = (await getEthTime(ethers.provider)) + 100 diff --git a/packages/contracts/test/contracts/L1/rollup/ChainStorageContainer.spec.ts b/packages/contracts/test/contracts/L1/rollup/ChainStorageContainer.spec.ts new file mode 100644 index 0000000000000..09d1c3c38d5c3 --- /dev/null +++ b/packages/contracts/test/contracts/L1/rollup/ChainStorageContainer.spec.ts @@ -0,0 +1,120 @@ +/* External Imports */ +import { ethers } from 'hardhat' +import { Contract, Signer, ContractFactory } from 'ethers' + +/* Internal Imports */ +import { expect } from '../../../setup' +import { makeAddressManager, NON_NULL_BYTES32 } from '../../../helpers' + +describe('ChainStorageContainer', () => { + let sequencer: Signer + let otherSigner: Signer + let signer: Signer + let signerAddress: string + + let AddressManager: Contract + let Factory__ChainStorageContainer: ContractFactory + before(async () => { + ;[sequencer, otherSigner, signer] = await ethers.getSigners() + signerAddress = await otherSigner.getAddress() + + AddressManager = await makeAddressManager() + await AddressManager.setAddress( + 'OVM_Sequencer', + await sequencer.getAddress() + ) + + Factory__ChainStorageContainer = await ethers.getContractFactory( + 'ChainStorageContainer' + ) + }) + + let ChainStorageContainer: Contract + beforeEach(async () => { + ChainStorageContainer = await Factory__ChainStorageContainer.connect( + otherSigner + ).deploy(AddressManager.address, signerAddress) + + await AddressManager.setAddress( + 'ChainStorageContainer', + ChainStorageContainer.address + ) + + await AddressManager.setAddress(signerAddress, signerAddress) + }) + + describe('push', () => { + for (const len of [1, 2, 4, 8, 32]) { + it(`it should be able to add ${len} element(s) to the array`, async () => { + for (let i = 0; i < len; i++) { + await expect( + ChainStorageContainer.connect(otherSigner)['push(bytes32)']( + NON_NULL_BYTES32 + ) + ).to.not.be.reverted + } + }) + } + }) + + describe('setGlobalMetadata', () => { + it('should modify the extra data', async () => { + const globalMetaData = `0x${'11'.repeat(27)}` + await ChainStorageContainer.connect(otherSigner).setGlobalMetadata( + globalMetaData + ) + + expect(await ChainStorageContainer.getGlobalMetadata()).to.equal( + globalMetaData + ) + }) + }) + + describe('deleteElementsAfterInclusive', () => { + it('should revert when the array is empty', async () => { + await expect( + ChainStorageContainer.connect(otherSigner)[ + 'deleteElementsAfterInclusive(uint256)' + ](0) + ).to.be.reverted + }) + + it('should revert when called by non-owner', async () => { + await expect( + ChainStorageContainer.connect(signer)[ + 'deleteElementsAfterInclusive(uint256)' + ](0) + ).to.be.revertedWith( + 'ChainStorageContainer: Function can only be called by the owner.' + ) + }) + + for (const len of [1, 2, 4, 8, 32]) { + describe(`when the array has ${len} element(s)`, () => { + const values = [] + beforeEach(async () => { + for (let i = 0; i < len; i++) { + const value = NON_NULL_BYTES32 + values.push(value) + await ChainStorageContainer.connect(otherSigner)['push(bytes32)']( + value + ) + } + }) + + for (let i = len - 1; i > 0; i -= Math.max(1, len / 4)) { + it(`should be able to delete everything after and including the ${i}th/st/rd/whatever element`, async () => { + await expect( + ChainStorageContainer.connect(otherSigner)[ + 'deleteElementsAfterInclusive(uint256)' + ](i) + ).to.not.be.reverted + + expect(await ChainStorageContainer.length()).to.equal(i) + await expect(ChainStorageContainer.get(i)).to.be.reverted + }) + } + }) + } + }) +}) diff --git a/packages/contracts/test/contracts/L1/rollup/StateCommitmentChain.spec.ts b/packages/contracts/test/contracts/L1/rollup/StateCommitmentChain.spec.ts index 43863dbdbaf1a..04f6152ec2981 100644 --- a/packages/contracts/test/contracts/L1/rollup/StateCommitmentChain.spec.ts +++ b/packages/contracts/test/contracts/L1/rollup/StateCommitmentChain.spec.ts @@ -409,17 +409,97 @@ describe('StateCommitmentChain', () => { }) }) - describe('verifyElement()', () => { + describe('getLastSequencerTimestamp', () => { + describe('when no batches have been inserted', () => { + it('should return zero', async () => { + expect(await StateCommitmentChain.getLastSequencerTimestamp()).to.equal( + 0 + ) + }) + }) + + describe('when one batch element has been inserted', () => { + let timestamp + beforeEach(async () => { + const batch = [NON_NULL_BYTES32] + Mock__CanonicalTransactionChain.smocked.getTotalElements.will.return.with( + batch.length + ) + await StateCommitmentChain.appendStateBatch(batch, 0) + timestamp = await getEthTime(ethers.provider) + }) + + it('should return the number of inserted batch elements', async () => { + expect(await StateCommitmentChain.getLastSequencerTimestamp()).to.equal( + timestamp + ) + }) + }) + }) + + describe('verifyStateCommitment()', () => { + const batch = [NON_NULL_BYTES32] + const batchHeader = { + batchIndex: 0, + batchRoot: NON_NULL_BYTES32, + batchSize: 1, + prevTotalElements: 0, + extraData: ethers.constants.HashZero, + } + + const inclusionProof = { + index: 0, + siblings: [], + } + + const element = NON_NULL_BYTES32 + + before(async () => { + Mock__BondManager.smocked.isCollateralized.will.return.with(true) + }) + + beforeEach(async () => { + Mock__CanonicalTransactionChain.smocked.getTotalElements.will.return.with( + batch.length + ) + await StateCommitmentChain.appendStateBatch(batch, 0) + batchHeader.extraData = ethers.utils.defaultAbiCoder.encode( + ['uint256', 'address'], + [await getEthTime(ethers.provider), await sequencer.getAddress()] + ) + }) + it('should revert when given an invalid batch header', async () => { - // TODO + await expect( + StateCommitmentChain.verifyStateCommitment( + element, + { + ...batchHeader, + extraData: '0x' + '22'.repeat(32), + }, + inclusionProof + ) + ).to.be.revertedWith('Invalid batch header.') }) it('should revert when given an invalid inclusion proof', async () => { - // TODO + await expect( + StateCommitmentChain.verifyStateCommitment( + ethers.constants.HashZero, + batchHeader, + inclusionProof + ) + ).to.be.revertedWith('Invalid inclusion proof.') }) it('should return true when given a valid proof', async () => { - // TODO + expect( + await StateCommitmentChain.verifyStateCommitment( + element, + batchHeader, + inclusionProof + ) + ).to.equal(true) }) }) }) diff --git a/packages/contracts/test/contracts/L2/messaging/L2CrossDomainMessenger.spec.ts b/packages/contracts/test/contracts/L2/messaging/L2CrossDomainMessenger.spec.ts index 8fa03793a9e10..4148928a29768 100644 --- a/packages/contracts/test/contracts/L2/messaging/L2CrossDomainMessenger.spec.ts +++ b/packages/contracts/test/contracts/L2/messaging/L2CrossDomainMessenger.spec.ts @@ -3,6 +3,7 @@ import hre, { ethers } from 'hardhat' import { Signer, ContractFactory, Contract } from 'ethers' import { smockit, MockContract } from '@eth-optimism/smock' import { applyL1ToL2Alias } from '@eth-optimism/core-utils' +import { smock, MockContractFactory } from '@defi-wonderland/smock' /* Internal Imports */ import { expect } from '../../../setup' @@ -67,6 +68,30 @@ describe('L2CrossDomainMessenger', () => { ) }) + describe('xDomainMessageSender', () => { + let Mock__Factory__L2CrossDomainMessenger: MockContractFactory + let Mock__L2CrossDomainMessenger + before(async () => { + Mock__Factory__L2CrossDomainMessenger = await smock.mock( + 'L2CrossDomainMessenger' + ) + Mock__L2CrossDomainMessenger = + await Mock__Factory__L2CrossDomainMessenger.deploy( + Mock__L1CrossDomainMessenger.address + ) + }) + + it('should return the xDomainMsgSender address', async () => { + await Mock__L2CrossDomainMessenger.setVariable( + 'xDomainMsgSender', + '0x0000000000000000000000000000000000000000' + ) + expect( + await Mock__L2CrossDomainMessenger.xDomainMessageSender() + ).to.equal('0x0000000000000000000000000000000000000000') + }) + }) + describe('sendMessage', () => { const target = NON_ZERO_ADDRESS const message = NON_NULL_BYTES32 diff --git a/packages/contracts/test/contracts/L2/messaging/L2StandardBridge.spec.ts b/packages/contracts/test/contracts/L2/messaging/L2StandardBridge.spec.ts index f63f45730790d..9819d669a17a7 100644 --- a/packages/contracts/test/contracts/L2/messaging/L2StandardBridge.spec.ts +++ b/packages/contracts/test/contracts/L2/messaging/L2StandardBridge.spec.ts @@ -7,6 +7,7 @@ import { smoddit, ModifiableContract, } from '@eth-optimism/smock' +import { smock } from '@defi-wonderland/smock' /* Internal Imports */ import { expect } from '../../../setup' @@ -20,6 +21,7 @@ const DUMMY_L1BRIDGE_ADDRESS: string = '0x1234123412341234123412341234123412341234' const DUMMY_L1TOKEN_ADDRESS: string = '0x2234223412342234223422342234223422342234' +const OVM_ETH_ADDRESS: string = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000' describe('L2StandardBridge', () => { let alice: Signer @@ -182,6 +184,15 @@ describe('L2StandardBridge', () => { describe('withdrawals', () => { const withdrawAmount = 1_000 let SmoddedL2Token: ModifiableContract + + let Fake__OVM_ETH + + before(async () => { + Fake__OVM_ETH = await smock.fake('OVM_ETH', { + address: OVM_ETH_ADDRESS, + }) + }) + beforeEach(async () => { // Deploy a smodded gateway so we can give some balances to withdraw SmoddedL2Token = await ( @@ -203,6 +214,35 @@ describe('L2StandardBridge', () => { }) }) + it('withdraw() withdraws and sends the correct withdrawal message for OVM_ETH', async () => { + await L2StandardBridge.withdraw( + Fake__OVM_ETH.address, + 0, + 0, + NON_NULL_BYTES32 + ) + + const withdrawalCallToMessenger = + Mock__L2CrossDomainMessenger.smocked.sendMessage.calls[0] + + // Assert the correct cross-chain call was sent: + // Message should be sent to the L1L1StandardBridge on L1 + expect(withdrawalCallToMessenger._target).to.equal(DUMMY_L1BRIDGE_ADDRESS) + + // Message data should be a call telling the L1StandardBridge to finalize the withdrawal + expect(withdrawalCallToMessenger._message).to.equal( + Factory__L1StandardBridge.interface.encodeFunctionData( + 'finalizeETHWithdrawal', + [ + await alice.getAddress(), + await alice.getAddress(), + 0, + NON_NULL_BYTES32, + ] + ) + ) + }) + it('withdraw() burns and sends the correct withdrawal message', async () => { await L2StandardBridge.withdraw( SmoddedL2Token.address, diff --git a/packages/contracts/test/contracts/L2/predeploys/WETH9.spec.ts b/packages/contracts/test/contracts/L2/predeploys/WETH9.spec.ts new file mode 100644 index 0000000000000..fc50b1199a9ff --- /dev/null +++ b/packages/contracts/test/contracts/L2/predeploys/WETH9.spec.ts @@ -0,0 +1,113 @@ +/* External Imports */ +import { ethers } from 'hardhat' +import { Contract, Signer, ContractFactory } from 'ethers' +import { + smock, + MockContractFactory, + MockContract, +} from '@defi-wonderland/smock' + +/* Internal Imports */ +import { expect } from '../../../setup' + +describe('WETH9', () => { + let signer: Signer + let otherSigner: Signer + let signerAddress: string + let otherSignerAddress: string + + let Mock__Factory_WETH9: MockContractFactory + let Mock__WETH9: MockContract + before(async () => { + ;[signer, otherSigner] = await ethers.getSigners() + signerAddress = await signer.getAddress() + otherSignerAddress = await otherSigner.getAddress() + }) + + beforeEach(async () => { + Mock__Factory_WETH9 = await smock.mock('WETH9') + Mock__WETH9 = await Mock__Factory_WETH9.deploy() + }) + + describe('deposit', () => { + it('should create WETH with fallback function', async () => { + await expect( + signer.sendTransaction({ + to: Mock__WETH9.address, + value: 200, + }) + ).to.not.be.reverted + + expect(await Mock__WETH9.balanceOf(signerAddress)).to.be.equal(200) + }) + + it('should create WETH with deposit function', async () => { + await expect(Mock__WETH9.connect(signer).deposit({ value: 100 })).to.not + .be.reverted + + expect(await Mock__WETH9.balanceOf(signerAddress)).to.be.equal(100) + }) + }) + + describe('withdraw', () => { + it('should revert when withdraw amount is bigger than balance', async () => { + await expect(Mock__WETH9.connect(signer).withdraw(10000)).to.be.reverted + }) + + it('should withdraw to eth', async () => { + await Mock__WETH9.connect(signer).deposit({ value: 100 }) + await expect(Mock__WETH9.connect(signer).withdraw(50)).to.not.be.reverted + expect(await Mock__WETH9.balanceOf(signerAddress)).to.be.equal(50) + }) + }) + + describe('totalSupply', () => { + it('should return the totalSupply', async () => { + await expect(Mock__WETH9.totalSupply()).to.not.be.reverted + }) + }) + + describe('transfer', () => { + it('should revert when sending more than deposited', async () => { + await Mock__WETH9.connect(signer).deposit({ value: 100 }) + await expect( + Mock__WETH9.connect(signer).transfer(otherSignerAddress, 500) + ).to.be.reverted + }) + + it('should transfer WETH to an other address', async () => { + await Mock__WETH9.connect(signer).deposit({ value: 100 }) + await expect(Mock__WETH9.connect(signer).transfer(otherSignerAddress, 50)) + .to.not.be.reverted + + expect(await Mock__WETH9.balanceOf(signerAddress)).to.be.equal(50) + + expect(await Mock__WETH9.balanceOf(otherSignerAddress)).to.be.equal(50) + }) + }) + + describe('transferFrom', () => { + it('should revert when there is no allowance', async () => { + await Mock__WETH9.connect(signer).deposit({ value: 100 }) + await expect( + Mock__WETH9.connect(otherSigner).transferFrom( + signerAddress, + otherSignerAddress, + 50 + ) + ).to.be.reverted + }) + + it('should transfer WETH to an other address when there is approvement', async () => { + await Mock__WETH9.connect(signer).deposit({ value: 100 }) + await Mock__WETH9.connect(signer).approve(otherSignerAddress, 50) + await expect( + Mock__WETH9.connect(otherSigner).transferFrom( + signerAddress, + otherSignerAddress, + 50 + ) + ).to.not.be.reverted + }) + }) +}) diff --git a/packages/contracts/test/contracts/libraries/codec/Lib_OVMCodec.spec.ts b/packages/contracts/test/contracts/libraries/codec/Lib_OVMCodec.spec.ts index b03a53f6531a0..8063890f7cc24 100644 --- a/packages/contracts/test/contracts/libraries/codec/Lib_OVMCodec.spec.ts +++ b/packages/contracts/test/contracts/libraries/codec/Lib_OVMCodec.spec.ts @@ -1,10 +1,39 @@ +/* External Imports */ +import { ethers } from 'hardhat' +import { Contract } from 'ethers' + /* Internal Imports */ -import '../../../setup' -import { Lib_OVMCodec_TEST_JSON } from '../../../data' -import { runJsonTest } from '../../../helpers' +import { expect } from '../../../setup' +import { NON_ZERO_ADDRESS } from '../../../helpers' + +describe('Lib_OVMCodec', () => { + let Lib_OVMCodec: Contract + before(async () => { + Lib_OVMCodec = await ( + await ethers.getContractFactory('TestLib_OVMCodec') + ).deploy() + }) + + describe('hashTransaction', () => { + enum QueueOrigin { + SEQUENCER_QUEUE, + L1TOL2_QUEUE, + } + + it('should return the hash of a transaction', async () => { + const tx = { + timestamp: 121212, + blockNumber: 10, + l1QueueOrigin: QueueOrigin.SEQUENCER_QUEUE, + l1TxOrigin: NON_ZERO_ADDRESS, + entrypoint: NON_ZERO_ADDRESS, + gasLimit: 100, + data: '0x1234', + } -describe.skip('Lib_OVMCodec', () => { - describe('JSON tests', () => { - runJsonTest('TestLib_OVMCodec', Lib_OVMCodec_TEST_JSON) + expect(await Lib_OVMCodec.hashTransaction(tx)).to.be.equal( + '0xf07818e2db63d0140e55c9e68cfaa030f9a2d0962f671d6b339edb2207633ebd' + ) + }) }) }) diff --git a/packages/contracts/test/contracts/libraries/rlp/Lib_RLPWriter.spec.ts b/packages/contracts/test/contracts/libraries/rlp/Lib_RLPWriter.spec.ts index 0933da97d5785..b4538a3557acd 100644 --- a/packages/contracts/test/contracts/libraries/rlp/Lib_RLPWriter.spec.ts +++ b/packages/contracts/test/contracts/libraries/rlp/Lib_RLPWriter.spec.ts @@ -38,6 +38,16 @@ describe('Lib_RLPWriter', () => { } }) + describe('writeBool', () => { + it(`should encode bool: true`, async () => { + expect(await Lib_RLPWriter.writeBool(true)).to.equal('0x01') + }) + + it(`should encode bool: false`, async () => { + expect(await Lib_RLPWriter.writeBool(false)).to.equal('0x80') + }) + }) + describe('Use of library with other memory-modifying operations', () => { it('should allow creation of a contract beforehand and still work', async () => { const randomAddress = '0x1234123412341234123412341234123412341234' diff --git a/packages/contracts/test/contracts/libraries/trie/Lib_SecureMerkleTrie.spec.ts b/packages/contracts/test/contracts/libraries/trie/Lib_SecureMerkleTrie.spec.ts index a82f74352e334..f8b238c7d3d3f 100644 --- a/packages/contracts/test/contracts/libraries/trie/Lib_SecureMerkleTrie.spec.ts +++ b/packages/contracts/test/contracts/libraries/trie/Lib_SecureMerkleTrie.spec.ts @@ -115,4 +115,22 @@ describe('Lib_SecureMerkleTrie', () => { }) } }) + + describe('getSingleNodeRootHash', () => { + let generator: TrieTestGenerator + before(async () => { + generator = await TrieTestGenerator.fromRandom({ + seed: `seed.get.${1}`, + nodeCount: 1, + secure: true, + }) + }) + + it(`should get the root hash of a trie with a single node`, async () => { + const test = await generator.makeInclusionProofTest(0) + expect( + await Lib_SecureMerkleTrie.getSingleNodeRootHash(test.key, test.val) + ).to.equal(test.root) + }) + }) }) diff --git a/packages/contracts/test/contracts/libraries/utils/Lib_Buffer.spec.ts b/packages/contracts/test/contracts/libraries/utils/Lib_Buffer.spec.ts index 6020d38abb8c7..eed435993ea25 100644 --- a/packages/contracts/test/contracts/libraries/utils/Lib_Buffer.spec.ts +++ b/packages/contracts/test/contracts/libraries/utils/Lib_Buffer.spec.ts @@ -12,12 +12,12 @@ describe('Lib_Buffer', () => { Lib_Buffer = await Factory__Lib_Buffer.deploy() }) - describe('push', () => { + describe('push(bytes32,bytes27)', () => { for (const len of [1, 2, 4, 8, 32]) { it(`it should be able to add ${len} element(s) to the array`, async () => { for (let i = 0; i < len; i++) { await expect( - Lib_Buffer.push( + Lib_Buffer['push(bytes32,bytes27)']( ethers.utils.keccak256(`0x${i.toString(16).padStart(16, '0')}`), `0x${'00'.repeat(27)}` ) @@ -27,6 +27,20 @@ describe('Lib_Buffer', () => { } }) + describe('push(bytes32)', () => { + for (const len of [1, 2, 4, 8, 32]) { + it(`it should be able to add ${len} element(s) to the array`, async () => { + for (let i = 0; i < len; i++) { + await expect( + Lib_Buffer['push(bytes32)']( + ethers.utils.keccak256(`0x${i.toString(16).padStart(16, '0')}`) + ) + ).to.not.be.reverted + } + }) + } + }) + describe('get', () => { for (const len of [1, 2, 4, 8, 32]) { describe(`when the array has ${len} element(s)`, () => { @@ -37,7 +51,10 @@ describe('Lib_Buffer', () => { `0x${i.toString(16).padStart(16, '0')}` ) values.push(value) - await Lib_Buffer.push(value, `0x${'00'.repeat(27)}`) + await Lib_Buffer['push(bytes32,bytes27)']( + value, + `0x${'00'.repeat(27)}` + ) } }) @@ -68,7 +85,10 @@ describe('Lib_Buffer', () => { `0x${i.toString(16).padStart(16, '0')}` ) values.push(value) - await Lib_Buffer.push(value, `0x${'00'.repeat(27)}`) + await Lib_Buffer['push(bytes32,bytes27)']( + value, + `0x${'00'.repeat(27)}` + ) } }) @@ -86,20 +106,26 @@ describe('Lib_Buffer', () => { it('should change if set by a call to push()', async () => { const extraData = `0x${'11'.repeat(27)}` - await Lib_Buffer.push(ethers.utils.keccak256('0x00'), extraData) + await Lib_Buffer['push(bytes32,bytes27)']( + ethers.utils.keccak256('0x00'), + extraData + ) expect(await Lib_Buffer.getExtraData()).to.equal(extraData) }) it('should change if set multiple times', async () => { - await Lib_Buffer.push( + await Lib_Buffer['push(bytes32,bytes27)']( ethers.utils.keccak256('0x00'), `0x${'11'.repeat(27)}` ) const extraData = `0x${'22'.repeat(27)}` - await Lib_Buffer.push(ethers.utils.keccak256('0x00'), extraData) + await Lib_Buffer['push(bytes32,bytes27)']( + ethers.utils.keccak256('0x00'), + extraData + ) expect(await Lib_Buffer.getExtraData()).to.equal(extraData) }) @@ -140,7 +166,10 @@ describe('Lib_Buffer', () => { `0x${i.toString(16).padStart(16, '0')}` ) values.push(value) - await Lib_Buffer.push(value, `0x${'00'.repeat(27)}`) + await Lib_Buffer['push(bytes32,bytes27)']( + value, + `0x${'00'.repeat(27)}` + ) } }) @@ -172,4 +201,26 @@ describe('Lib_Buffer', () => { }) } }) + + describe('setContext', () => { + it('should modify the context', async () => { + const length = 20 + const extraData = `0x${'11'.repeat(27)}` + const cntx = [length, extraData] + + await Lib_Buffer.setContext(length, extraData) + + expect(await Lib_Buffer.getContext()).to.eql(cntx) + }) + + it('should not modify the context', async () => { + const length = 0 + const extraData = `0x${'00'.repeat(27)}` + + const prevContext = await Lib_Buffer.getContext() + await Lib_Buffer.setContext(length, extraData) + + expect(await Lib_Buffer.getContext()).to.eql(prevContext) + }) + }) }) diff --git a/packages/contracts/test/contracts/libraries/utils/Lib_MerkleTree.spec.ts b/packages/contracts/test/contracts/libraries/utils/Lib_MerkleTree.spec.ts index 34f82b715b8ba..6d091194ab17a 100644 --- a/packages/contracts/test/contracts/libraries/utils/Lib_MerkleTree.spec.ts +++ b/packages/contracts/test/contracts/libraries/utils/Lib_MerkleTree.spec.ts @@ -3,6 +3,7 @@ import { ethers } from 'hardhat' import { Contract, BigNumber } from 'ethers' import { MerkleTree } from 'merkletreejs' import { fromHexString, toHexString } from '@eth-optimism/core-utils' +import { smock, FakeContract } from '@defi-wonderland/smock' /* Internal Imports */ import { expect } from '../../../setup' @@ -32,10 +33,15 @@ const fillDefaultHashes = (elements: string[]): string[] => { describe('Lib_MerkleTree', () => { let Lib_MerkleTree: Contract + let Fake__LibMerkleTree: FakeContract before(async () => { Lib_MerkleTree = await ( await ethers.getContractFactory('TestLib_MerkleTree') ).deploy() + + Fake__LibMerkleTree = await smock.fake( + await ethers.getContractFactory('TestLib_MerkleTree') + ) }) describe('getMerkleRoot', () => { @@ -78,6 +84,19 @@ describe('Lib_MerkleTree', () => { }) } }) + + describe('when odd number of elements is provided', () => { + it(`should generate the correct root when odd number of elements are provided`, async () => { + const elements = ['0x12', '0x34', '0x56'].map((value) => + ethers.utils.keccak256(value) + ) + + Fake__LibMerkleTree.getMerkleRoot.returns() + + // expect(await Fake__LibMerkleTree.getMerkleRoot(elements)).to.not.be.reverted + await expect(Lib_MerkleTree.getMerkleRoot(elements)).to.not.be.reverted + }) + }) }) describe('verify', () => { @@ -99,6 +118,24 @@ describe('Lib_MerkleTree', () => { }) }) + describe('when total elements is zero', () => { + const totalLeaves = 0 + + it('should revert', async () => { + await expect( + Lib_MerkleTree.verify( + ethers.constants.HashZero, + ethers.constants.HashZero, + 0, + [], + totalLeaves + ) + ).to.be.revertedWith( + 'Lib_MerkleTree: Total leaves must be greater than zero.' + ) + }) + }) + describe('when an index is out of bounds', () => { const totalLeaves = 1 const index = 2 diff --git a/packages/contracts/test/data/index.ts b/packages/contracts/test/data/index.ts index 9f67a69ccca1b..de2d456fd5531 100644 --- a/packages/contracts/test/data/index.ts +++ b/packages/contracts/test/data/index.ts @@ -3,4 +3,3 @@ export { tests as Lib_RLPReader_TEST_JSON } from './json/libraries/rlp/Lib_RLPRe export { tests as Lib_Bytes32Utils_TEST_JSON } from './json/libraries/utils/Lib_Bytes32Utils.test.json' export { tests as Lib_BytesUtils_TEST_JSON } from './json/libraries/utils/Lib_BytesUtils.test.json' export { tests as Lib_MerkleTrie_TEST_JSON } from './json/libraries/trie/Lib_MerkleTrie.test.json' -export { tests as Lib_OVMCodec_TEST_JSON } from './json/libraries/codec/Lib_OVMCodec.test.json' diff --git a/packages/contracts/test/data/json/libraries/codec/Lib_OVMCodec.test.json b/packages/contracts/test/data/json/libraries/codec/Lib_OVMCodec.test.json deleted file mode 100644 index a55ce0122c21f..0000000000000 --- a/packages/contracts/test/data/json/libraries/codec/Lib_OVMCodec.test.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "tests": { - } -}