From 6b970a1bf30f615875f28f31d507c457b49bc27c Mon Sep 17 00:00:00 2001 From: aalavandhan1984 Date: Mon, 25 Oct 2021 12:12:20 -0400 Subject: [PATCH] arb gateway unit tests --- .../bridge-gateways/ampl_arbitrum_gateway.js | 392 ++++++++++++++++++ .../arbitrum_xcampl_gateway.js | 320 ++++++++++++++ 2 files changed, 712 insertions(+) create mode 100644 test/unit/base-chain/bridge-gateways/ampl_arbitrum_gateway.js create mode 100644 test/unit/satellite-chain/bridge-gateways/arbitrum_xcampl_gateway.js diff --git a/test/unit/base-chain/bridge-gateways/ampl_arbitrum_gateway.js b/test/unit/base-chain/bridge-gateways/ampl_arbitrum_gateway.js new file mode 100644 index 0000000..a7ca156 --- /dev/null +++ b/test/unit/base-chain/bridge-gateways/ampl_arbitrum_gateway.js @@ -0,0 +1,392 @@ +const { ethers } = require('hardhat'); +const { expect } = require('chai'); + +let accounts, + deployer, + depositor, + depositorAddress, + recipient, + recipientAddress, + router, + routerAddress, + counterpartGateway, + counterpartAddress, + inbox, + ampl, + policy, + vault, + xcAmple, + gateway; + +// l2 gas paramters +const maxSubmissionCost = 1; +const maxGas = 500000; +const gasPriceBid = 0; + +async function setupContracts() { + accounts = await ethers.getSigners(); + deployer = accounts[0]; + depositor = deployer; + depositorAddress = await deployer.getAddress(); + recipient = accounts[1]; + recipientAddress = await recipient.getAddress(); + router = accounts[2]; + routerAddress = await router.getAddress(); + counterpartGateway = accounts[3]; + counterpartAddress = await counterpartGateway.getAddress(); + + inbox = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockArbitrum.sol:MockArbitrumInbox', + ) + ) + .connect(deployer) + .deploy(); + + const uFragmentsFactory = await ethers.getContractFactory( + 'uFragments/contracts/UFragments.sol:UFragments', + ); + ampl = await upgrades.deployProxy( + uFragmentsFactory.connect(deployer), + [depositorAddress], + { initializer: 'initialize(address)' }, + ); + await ampl.setMonetaryPolicy(depositorAddress); + + policy = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockAmpleforth.sol:MockAmpleforth', + ) + ) + .connect(deployer) + .deploy(); + vault = await ( + await ethers.getContractFactory('contracts/_mocks/MockVault.sol:MockVault') + ) + .connect(deployer) + .deploy(); + + xcAmple = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockXCAmple.sol:MockXCAmple', + ) + ) + .connect(deployer) + .deploy(); + + gateway = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockArbitrum.sol:MockAMPLArbitrumGateway', + ) + ) + .connect(deployer) + .deploy(ampl.address, policy.address, vault.address); + + await gateway.initialize( + inbox.address, + routerAddress, + xcAmple.address, + counterpartAddress, + ); + await inbox.setL2ToL1Sender(counterpartAddress); + + await policy.updateEpoch(1); + await ampl.rebase(1, '-49999999999950000'); +} + +describe('AMPLArbitrumGateway:Initialization', () => { + before('setup AMPLArbitrumGateway contract', setupContracts); + + it('should initialize the references', async function () { + expect(await gateway.ampl()).to.eq(ampl.address); + expect(await gateway.policy()).to.eq(policy.address); + expect(await gateway.vault()).to.eq(vault.address); + expect(await gateway.inbox()).to.eq(inbox.address); + expect(await gateway.router()).to.eq(routerAddress); + expect(await gateway.counterpartGateway()).to.eq(counterpartAddress); + expect(await gateway.xcAmple()).to.eq(xcAmple.address); + expect(await gateway.calculateL2TokenAddress(ampl.address)).to.eq( + xcAmple.address, + ); + }); +}); + +describe('AMPLArbitrumGateway:reportRebaseInit', () => { + let r, seqNumber; + before('setup AMPLArbitrumGateway contract', async function () { + await setupContracts(); + seqNumber = await gateway + .connect(depositor) + .callStatic.reportRebaseInit(maxSubmissionCost, maxGas, gasPriceBid); + r = gateway + .connect(depositor) + .reportRebaseInit(maxSubmissionCost, maxGas, gasPriceBid); + }); + + it('should emit XCRebaseReportOut', async function () { + await expect(r).to.emit(gateway, 'XCRebaseReportOut').withArgs(1, 50000); + }); + + it('should emit RebaseReportInitiated', async function () { + await expect(r) + .to.emit(gateway, 'RebaseReportInitiated') + .withArgs(seqNumber); + }); +}); + +describe('AMPLArbitrumGateway:outboundTransfer:accessControl', () => { + before('setup AMPLArbitrumGateway contract', setupContracts); + + it('should NOT be callable by non-router', async function () { + await expect( + gateway + .connect(depositor) + .outboundTransfer( + ampl.address, + recipientAddress, + 1001, + maxGas, + gasPriceBid, + [], + ), + ).to.be.revertedWith('AMPLArbitrumGateway: NOT_FROM_ROUTER'); + }); + + it('should NOT be callable for other tokens', async function () { + await expect( + gateway + .connect(router) + .outboundTransfer( + xcAmple.address, + recipientAddress, + 1001, + maxGas, + gasPriceBid, + [], + ), + ).to.be.revertedWith('AMPLArbitrumGateway: ONLY_AMPL_ALLOWED'); + }); + + it('should NOT allow extra data', async function () { + let data = ethers.utils.defaultAbiCoder.encode( + ['uint256', 'bytes'], + [ + maxSubmissionCost, + ethers.utils.defaultAbiCoder.encode(['uint256'], ['123']), + ], + ); + + data = ethers.utils.defaultAbiCoder.encode( + ['address', 'bytes'], + [depositorAddress, data], + ); + + await expect( + gateway + .connect(router) + .outboundTransfer( + ampl.address, + recipientAddress, + 1001, + maxGas, + gasPriceBid, + data, + ), + ).to.be.revertedWith('AMPLArbitrumGateway: EXTRA_DATA_DISABLED'); + }); +}); + +describe('AMPLArbitrumGateway:outboundTransfer', () => { + let r, seqNumber; + before('setup AMPLArbitrumGateway contract', async function () { + await setupContracts(); + + // router usually does this encoding part + let data = ethers.utils.defaultAbiCoder.encode( + ['uint256', 'bytes'], + [maxSubmissionCost, '0x'], + ); + + data = ethers.utils.defaultAbiCoder.encode( + ['address', 'bytes'], + [depositorAddress, data], + ); + + await ampl.connect(depositor).approve(gateway.address, 1001); + + seqNumber = await gateway + .connect(router) + .callStatic.outboundTransfer( + ampl.address, + recipientAddress, + 1001, + maxGas, + gasPriceBid, + data, + ); + r = gateway + .connect(router) + .outboundTransfer( + ampl.address, + recipientAddress, + 1001, + maxGas, + gasPriceBid, + data, + ); + }); + + it('should emit XCTransferOut', async function () { + await expect(r) + .to.emit(gateway, 'XCTransferOut') + .withArgs(depositorAddress, ethers.constants.AddressZero, 1001, 50000); + }); + + it('should emit DepositInitiated', async function () { + await expect(r) + .to.emit(gateway, 'DepositInitiated') + .withArgs( + ampl.address, + depositorAddress, + recipientAddress, + seqNumber, + 1001, + ); + }); + + it('should lock into vault', async function () { + await expect(r) + .to.emit(vault, 'Lock') + .withArgs(ampl.address, depositorAddress, 1001); + }); +}); + +describe('AMPLArbitrumGateway:finalizeInboundTransfer:accessControl', () => { + before('setup AMPLArbitrumGateway contract', setupContracts); + + it('should revert when called by non counterpart', async function () { + const r = gateway + .connect(deployer) + .finalizeInboundTransfer( + ampl.address, + depositorAddress, + recipientAddress, + 1001, + [], + ); + await expect(r).to.be.revertedWith( + 'AMPLArbitrumGateway: ONLY_COUNTERPART_GATEWAY', + ); + }); +}); + +describe('AMPLArbitrumGateway:finalizeInboundTransfer', () => { + let r, seqNumber; + + describe('when supply is out of sync', function () { + before('setup AMPLArbitrumGateway contract', async function () { + await setupContracts(); + + const exitNum = 123213213; + const withdrawData = ethers.utils.defaultAbiCoder.encode( + ['uint256', 'uint256'], + [exitNum, 50000], + ); + + await policy.updateEpoch(2); + await ampl.rebase(2, 50000); + + r = gateway + .connect(counterpartGateway) + .finalizeInboundTransfer( + ampl.address, + depositorAddress, + recipientAddress, + 1001, + withdrawData, + ); + }); + + it('should emit XCTransferIn', async function () { + await expect(r) + .to.emit(gateway, 'XCTransferIn') + .withArgs( + ethers.constants.AddressZero, + recipientAddress, + 50000, + 1001, + 100000, + ); + }); + + it('should emit WithdrawalFinalized', async function () { + await expect(r) + .to.emit(gateway, 'WithdrawalFinalized') + .withArgs( + ampl.address, + depositorAddress, + recipientAddress, + 123213213, + 2002, + ); + }); + + it('should unlock from vault', async function () { + await expect(r) + .to.emit(vault, 'Unlock') + .withArgs(ampl.address, recipientAddress, 2002); + }); + }); + + describe('when supply is in sync', function () { + before('setup AMPLArbitrumGateway contract', async function () { + await setupContracts(); + + const exitNum = 89324; + const withdrawData = ethers.utils.defaultAbiCoder.encode( + ['uint256', 'uint256'], + [exitNum, 50000], + ); + + r = gateway + .connect(counterpartGateway) + .finalizeInboundTransfer( + ampl.address, + depositorAddress, + recipientAddress, + 1001, + withdrawData, + ); + }); + + it('should emit XCTransferIn', async function () { + await expect(r) + .to.emit(gateway, 'XCTransferIn') + .withArgs( + ethers.constants.AddressZero, + recipientAddress, + 50000, + 1001, + 50000, + ); + }); + + it('should emit WithdrawalFinalized', async function () { + await expect(r) + .to.emit(gateway, 'WithdrawalFinalized') + .withArgs( + ampl.address, + depositorAddress, + recipientAddress, + 89324, + 1001, + ); + }); + + it('should unlock from vault', async function () { + await expect(r) + .to.emit(vault, 'Unlock') + .withArgs(ampl.address, recipientAddress, 1001); + }); + }); +}); diff --git a/test/unit/satellite-chain/bridge-gateways/arbitrum_xcampl_gateway.js b/test/unit/satellite-chain/bridge-gateways/arbitrum_xcampl_gateway.js new file mode 100644 index 0000000..ad3dc81 --- /dev/null +++ b/test/unit/satellite-chain/bridge-gateways/arbitrum_xcampl_gateway.js @@ -0,0 +1,320 @@ +const { ethers } = require('hardhat'); +const { expect } = require('chai'); + +let accounts, + deployer, + depositor, + depositorAddress, + recipient, + recipientAddress, + router, + routerAddress, + counterpartGateway, + counterpartAddress, + ampl, + xcAmple, + xcController, + gateway; + +async function setupContracts() { + accounts = await ethers.getSigners(); + deployer = accounts[0]; + depositor = deployer; + depositorAddress = await deployer.getAddress(); + recipient = accounts[1]; + recipientAddress = await recipient.getAddress(); + router = accounts[2]; + routerAddress = await router.getAddress(); + counterpartGateway = accounts[3]; + counterpartAddress = await counterpartGateway.getAddress(); + + xcAmple = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockXCAmple.sol:MockXCAmple', + ) + ) + .connect(deployer) + .deploy(); + xcController = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockXCAmpleController.sol:MockXCAmpleController', + ) + ) + .connect(deployer) + .deploy(); + + ampl = await ( + await ethers.getContractFactory('contracts/_mocks/MockAmpl.sol:MockAmpl') + ) + .connect(deployer) + .deploy(); + + gateway = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockArbitrum.sol:MockArbitrumXCAmpleGateway', + ) + ) + .connect(deployer) + .deploy(xcAmple.address, xcController.address); + + await gateway.initialize(routerAddress, ampl.address, counterpartAddress); + + await ampl.updateTotalSupply(50000); + await xcController.updateAMPLEpoch(1); + await xcAmple.updateGlobalAMPLSupply(50000); +} + +describe('ArbitrumXCAmpleGateway:Initialization', () => { + before('setup ArbitrumXCAmpleGateway contract', setupContracts); + + it('should initialize the references', async function () { + expect(await gateway.xcAmple()).to.eq(xcAmple.address); + expect(await gateway.xcController()).to.eq(xcController.address); + expect(await gateway.ampl()).to.eq(ampl.address); + expect(await gateway.router()).to.eq(routerAddress); + expect(await gateway.counterpartGateway()).to.eq(counterpartAddress); + }); +}); + +describe('ArbitrumXCAmpleGateway:reportRebaseCommit:accessControl', () => { + before('setup ArbitrumXCAmpleGateway contract', setupContracts); + + it('should NOT be callable by non gateway', async function () { + await expect( + gateway.connect(deployer).reportRebaseCommit(1, 50000), + ).to.be.revertedWith('ArbitrumXCAmpleGateway: ONLY_COUNTERPART_GATEWAY'); + }); +}); + +describe('ArbitrumXCAmpleGateway:reportRebaseCommit', () => { + before('setup ArbitrumXCAmpleGateway contract', setupContracts); + + describe('when on-chain supply is different', async function () { + it('should emit XCRebaseReportIn', async function () { + await expect( + gateway.connect(counterpartGateway).reportRebaseCommit(2, 100000), + ) + .to.emit(gateway, 'XCRebaseReportIn') + .withArgs(2, 100000, 1, 50000); + + await expect( + gateway.connect(counterpartGateway).reportRebaseCommit(3, 40000), + ) + .to.emit(gateway, 'XCRebaseReportIn') + .withArgs(3, 40000, 1, 50000); + }); + }); + + describe('when on-chain supply is the same', async function () { + it('should emit XCRebaseReportIn', async function () { + await expect( + gateway.connect(counterpartGateway).reportRebaseCommit(2, 50000), + ) + .to.emit(gateway, 'XCRebaseReportIn') + .withArgs(2, 50000, 1, 50000); + }); + }); +}); + +describe('ArbitrumXCAmpleGateway:outboundTransfer:accessControl', () => { + before('setup ArbitrumXCAmpleGateway contract', setupContracts); + + it('should NOT be callable by non-gateway', async function () { + await expect( + gateway + .connect(depositor) + .outboundTransfer(ampl.address, recipientAddress, 1001, 0, 0, []), + ).to.be.revertedWith('ArbitrumXCAmpleGateway: NOT_FROM_ROUTER'); + }); + + it('should NOT be callable for other tokens', async function () { + await expect( + gateway + .connect(router) + .outboundTransfer(xcAmple.address, recipientAddress, 1001, 0, 0, []), + ).to.be.revertedWith('ArbitrumXCAmpleGateway: ONLY_AMPL_ALLOWED'); + }); + + it('should NOT allow extra data', async function () { + const data = ethers.utils.defaultAbiCoder.encode( + ['address', 'bytes'], + [depositorAddress, '0x01'], + ); + + await expect( + gateway + .connect(router) + .outboundTransfer(ampl.address, recipientAddress, 1001, 0, 0, data), + ).to.be.revertedWith('ArbitrumXCAmpleGateway: EXTRA_DATA_DISABLED'); + }); +}); + +describe('ArbitrumXCAmpleGateway:outboundTransfer', () => { + let r, seqNumber, exitNum; + before('setup ArbitrumXCAmpleGateway contract', async function () { + await setupContracts(); + + // router usually does this encoding part + const data = ethers.utils.defaultAbiCoder.encode( + ['address', 'bytes'], + [depositorAddress, '0x'], + ); + + exitNum = await gateway.exitNum(); + + seqNumber = await gateway + .connect(router) + .callStatic.outboundTransfer( + ampl.address, + recipientAddress, + 1001, + 0, + 0, + data, + ); + + r = gateway + .connect(router) + .outboundTransfer(ampl.address, recipientAddress, 1001, 0, 0, data); + }); + + it('should emit XCTransferOut', async function () { + await expect(r) + .to.emit(gateway, 'XCTransferOut') + .withArgs(depositorAddress, ethers.constants.AddressZero, 1001, 50000); + }); + + it('should emit WithdrawalInitiated', async function () { + await expect(r) + .to.emit(gateway, 'WithdrawalInitiated') + .withArgs( + ampl.address, + depositorAddress, + recipientAddress, + seqNumber, + exitNum, + 1001, + ); + }); + + it('should increment exitNum', async function () { + expect(await gateway.exitNum()).to.eq(exitNum.add(1)); + }); + + it('should burn xcAmples', async function () { + await expect(r) + .to.emit(xcController, 'Burn') + .withArgs(depositorAddress, 1001); + }); +}); + +describe('ArbitrumXCAmpleGateway:finalizeInboundTransfer:accessControl', () => { + before('setup ArbitrumXCAmpleGateway contract', setupContracts); + + it('should revert when called by non counterpart', async function () { + const r = gateway + .connect(deployer) + .finalizeInboundTransfer( + ampl.address, + depositorAddress, + recipientAddress, + 1001, + [], + ); + await expect(r).to.be.revertedWith( + 'ArbitrumXCAmpleGateway: ONLY_COUNTERPART_GATEWAY', + ); + }); +}); + +describe('ArbitrumXCAmpleGateway:finalizeInboundTransfer', () => { + let r, seqNumber; + + describe('when supply is out of sync', function () { + before('setup ArbitrumXCAmpleGateway contract', async function () { + await setupContracts(); + + const exitNum = 0; + const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [50000]); + + await xcAmple.updateGlobalAMPLSupply(100000); + + r = gateway + .connect(counterpartGateway) + .finalizeInboundTransfer( + ampl.address, + depositorAddress, + recipientAddress, + 1001, + data, + ); + }); + + it('should emit XCTransferIn', async function () { + await expect(r) + .to.emit(gateway, 'XCTransferIn') + .withArgs( + ethers.constants.AddressZero, + recipientAddress, + 50000, + 2002, + 100000, + ); + }); + + it('should emit DepositFinalized', async function () { + await expect(r) + .to.emit(gateway, 'DepositFinalized') + .withArgs(ampl.address, depositorAddress, recipientAddress, 2002); + }); + + it('should mint', async function () { + await expect(r) + .to.emit(xcController, 'Mint') + .withArgs(recipientAddress, 2002); + }); + }); + + describe('when supply is in sync', function () { + before('setup ArbitrumXCAmpleGateway contract', async function () { + await setupContracts(); + + const exitNum = 0; + const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [50000]); + + r = gateway + .connect(counterpartGateway) + .finalizeInboundTransfer( + ampl.address, + depositorAddress, + recipientAddress, + 1001, + data, + ); + }); + + it('should emit XCTransferIn', async function () { + await expect(r) + .to.emit(gateway, 'XCTransferIn') + .withArgs( + ethers.constants.AddressZero, + recipientAddress, + 50000, + 1001, + 50000, + ); + }); + + it('should emit DepositFinalized', async function () { + await expect(r) + .to.emit(gateway, 'DepositFinalized') + .withArgs(ampl.address, depositorAddress, recipientAddress, 1001); + }); + + it('should mint', async function () { + await expect(r) + .to.emit(xcController, 'Mint') + .withArgs(recipientAddress, 1001); + }); + }); +});