diff --git a/contracts/UpdatedForwarder.sol b/contracts/UpdatedForwarder.sol index ccc7a7f..f1e11a8 100644 --- a/contracts/UpdatedForwarder.sol +++ b/contracts/UpdatedForwarder.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.15; +pragma solidity 0.8.20; import '@openzeppelin/contracts/token/ERC1155/IERC1155.sol'; import '@openzeppelin/contracts/token/ERC721/IERC721.sol'; import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol'; diff --git a/contracts/UpdatedForwarderFactory.sol b/contracts/UpdatedForwarderFactory.sol index 4eeb9ce..9ce7f4a 100644 --- a/contracts/UpdatedForwarderFactory.sol +++ b/contracts/UpdatedForwarderFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.15; +pragma solidity 0.8.20; import './UpdatedForwarder.sol'; import './CloneFactory.sol'; diff --git a/test/updatedForwarder.js b/test/updatedForwarder.js index 2e141e4..b9cb5d5 100644 --- a/test/updatedForwarder.js +++ b/test/updatedForwarder.js @@ -5,7 +5,8 @@ const helpers = require('./helpers'); const BigNumber = require('bignumber.js'); const { makeInterfaceId } = require('@openzeppelin/test-helpers'); -const Forwarder = artifacts.require('./Forwarder.sol'); +const UpdatedForwarder = artifacts.require('./UpdatedForwarder.sol'); +const UpdatedForwarderFactory = artifacts.require('./UpdatedForwarderFactory.sol'); const ERC721 = artifacts.require('./MockERC721'); const ERC1155 = artifacts.require('./MockERC1155'); const AlwaysFalseERC165 = artifacts.require('./AlwaysFalseERC165.sol'); @@ -14,12 +15,52 @@ const util = require('ethereumjs-util'); const abi = require('ethereumjs-abi'); const hre = require('hardhat'); -const createForwarder = async (creator, parent) => { - const forwarderContract = await Forwarder.new([], { from: creator }); - await forwarderContract.init(parent, true, true); +const createForwarder = async (creator, parent, feeAddress) => { + const forwarderContract = await UpdatedForwarder.new([], { from: creator }); + await forwarderContract.init(parent, feeAddress, true, true); return forwarderContract; }; +const createForwarderFactory = async () => { + const forwarderContract = await UpdatedForwarder.new([], {}); + const forwarderFactory = await UpdatedForwarderFactory.new( + forwarderContract.address + ); + return { + implementationAddress: forwarderContract.address, + factory: forwarderFactory + }; +}; + +const getForwarderAddressCreate2 = async ( + factory, + implementationAddress, + parent, + salt, +) => { + const inputSalt = util.setLengthLeft( + Buffer.from(util.stripHexPrefix(salt), 'hex'), + 32 + ); + + const calculationSalt = abi.soliditySHA3( + ['address', 'bytes32'], + [parent, inputSalt] + ); + + const initCode = helpers.getInitCode( + util.stripHexPrefix(implementationAddress) + ); + const forwarderAddress = helpers.getNextContractAddressCreate2( + factory.address, + calculationSalt, + initCode + ); + + return forwarderAddress; +}; + + const getBalanceInWei = async (address) => { return new BigNumber(await web3.eth.getBalance(address)); }; @@ -41,7 +82,7 @@ const getMethodData = async function (types, values, methodName) { const FORWARDER_DEPOSITED_EVENT = 'ForwarderDeposited'; -describe('Forwarder', function () { +describe('UpdatedForwarder', function () { let accounts; before(async () => { await hre.network.provider.send('hardhat_reset'); @@ -49,7 +90,7 @@ describe('Forwarder', function () { }); it('Basic forwarding test', async function () { - const forwarder = await createForwarder(accounts[0], accounts[0]); + const forwarder = await createForwarder(accounts[0], accounts[0], accounts[2]); const startBalance = await getBalanceInWei(accounts[0]); const amount = web3.utils.toWei('2', 'ether'); @@ -74,17 +115,25 @@ describe('Forwarder', function () { it('Flush on initialization', async function () { // determine the forwarder contract address const amount = web3.utils.toWei('5', 'ether'); - const baseAddress = accounts[3]; - const senderAddress = accounts[0]; - const forwarderAddress = await helpers.getNextContractAddress( - senderAddress + const baseAddress = accounts[1]; + const feeAddress = accounts[2]; + const factory = await createForwarderFactory(); + const forwarderAddress = await getForwarderAddressCreate2( + factory.factory, + factory.implementationAddress, + baseAddress, + '0x1234' + ); + const inputSalt = util.setLengthLeft( + Buffer.from(util.stripHexPrefix('0x1234'), 'hex'), + 32 ); const startBalance = await getBalanceInWei(baseAddress); // send funds to the contract address first await web3.eth.sendTransaction({ - from: accounts[2], + from: accounts[3], to: forwarderAddress, value: amount }); @@ -92,29 +141,23 @@ describe('Forwarder', function () { // Check that the ether is in the forwarder address and not yet in the base address (await getBalanceInWei(forwarderAddress)).eq(amount).should.be.true(); (await getBalanceInWei(baseAddress)).eq(startBalance).should.be.true(); - - const forwarder = await Forwarder.new([], { from: senderAddress }); - const tx = await forwarder.init(baseAddress, true, true); - forwarder.address.should.eql(forwarderAddress); + await factory.factory.createForwarder( + baseAddress, + feeAddress, + inputSalt, + true, + true, + ); // Check that the ether was automatically flushed to the base address (await getBalanceInWei(forwarderAddress)).eq(0).should.be.true(); (await getBalanceInWei(baseAddress)) .eq(startBalance.plus(amount)) .should.be.true(); - - const forwardedEvent = await helpers.getEventFromTransaction( - tx.receipt.transactionHash, - FORWARDER_DEPOSITED_EVENT - ); - - should.exist(forwardedEvent); - forwardedEvent.from.should.equal(forwarderAddress); - forwardedEvent.value.should.equal(amount); }); it('Should forward with data passed', async function () { - const forwarder = await createForwarder(accounts[0], accounts[0]); + const forwarder = await createForwarder(accounts[0], accounts[0], accounts[2]); const startBalance = await getBalanceInWei(accounts[0]); const amount = web3.utils.toWei('2', 'ether'); @@ -131,16 +174,26 @@ describe('Forwarder', function () { it('Should not init twice', async function () { const baseAddress = accounts[3]; - const forwarder = await createForwarder(baseAddress, baseAddress); + const feeAddress = accounts[4]; + const forwarder = await createForwarder(baseAddress, baseAddress, feeAddress); await truffleAssert.reverts( - forwarder.init(baseAddress, true, { from: baseAddress }) + forwarder.init(baseAddress, feeAddress, true, { from: baseAddress }) ); }); - it('should change autoFlush721 when calling setAutoFlush721', async () => { + it('Should not init if fee address is invalid address', async function () { const baseAddress = accounts[3]; - const forwarder = await createForwarder(baseAddress, baseAddress); + const feeAddress = accounts[4]; + const forwarder = await createForwarder(baseAddress, baseAddress, feeAddress); + + console.log('forwarder', forwarder); + }); + + it('should change autoFlush721 when calling setAutoFlush721 from allowed address', async () => { + const baseAddress = accounts[3]; + const feeAddress = accounts[4]; + const forwarder = await createForwarder(baseAddress, baseAddress, feeAddress); const initialState = await forwarder.autoFlush721(); await forwarder.setAutoFlush721(!initialState, { from: baseAddress }); @@ -149,32 +202,35 @@ describe('Forwarder', function () { initialState.should.equal(!newState); }); - it('should fail to toggle autoFlush721 if caller is not parent', async () => { + it('should fail to toggle autoFlush721 if caller is not allowed address', async () => { const baseAddress = accounts[3]; - const forwarder = await createForwarder(baseAddress, baseAddress); + const feeAddress = accounts[4]; + const forwarder = await createForwarder(baseAddress, baseAddress, feeAddress); await truffleAssert.reverts( - forwarder.setAutoFlush721(false, { from: accounts[4] }) + forwarder.setAutoFlush721(false, { from: accounts[5] }) ); }); - it('should toggle autoFlush1155 when calling setAutoFlush1155', async () => { + it('should toggle autoFlush1155 when calling setAutoFlush1155 from allowed address', async () => { const baseAddress = accounts[3]; - const forwarder = await createForwarder(baseAddress, baseAddress); + const feeAddress = accounts[4]; + const forwarder = await createForwarder(baseAddress, baseAddress, feeAddress); const initialState = await forwarder.autoFlush1155(); - await forwarder.setAutoFlush1155(!initialState, { from: baseAddress }); + await forwarder.setAutoFlush1155(!initialState, { from: feeAddress }); const newState = await forwarder.autoFlush1155(); initialState.should.equal(!newState); }); - it('should fail to toggle autoFlush1155 if caller is not parent', async () => { + it('should fail to toggle autoFlush1155 if caller is not allowed address', async () => { const baseAddress = accounts[3]; - const forwarder = await createForwarder(baseAddress, baseAddress); + const feeAddress = accounts[4]; + const forwarder = await createForwarder(baseAddress, baseAddress, feeAddress); await truffleAssert.reverts( - forwarder.setAutoFlush1155(false, { from: accounts[4] }) + forwarder.setAutoFlush1155(false, { from: accounts[5] }) ); }); @@ -182,6 +238,7 @@ describe('Forwarder', function () { let token721; let tokenId = 0; let baseAddress; + let feeAddress; let autoFlushForwarder; let noAutoFlushForwarder; @@ -190,9 +247,11 @@ describe('Forwarder', function () { const symbol = 'NFT'; token721 = await ERC721.new(name, symbol); baseAddress = accounts[0]; - autoFlushForwarder = await createForwarder(baseAddress, baseAddress); - noAutoFlushForwarder = await Forwarder.new([], { from: accounts[1] }); - await noAutoFlushForwarder.init(baseAddress, false, false); + feeAddress = accounts[2]; + + autoFlushForwarder = await createForwarder(baseAddress, baseAddress, feeAddress); + noAutoFlushForwarder = await UpdatedForwarder.new([], { from: accounts[1] }); + await noAutoFlushForwarder.init(baseAddress, feeAddress, false, false); }); it('Should support NFT safeTransferFrom function', async function () { @@ -280,7 +339,7 @@ describe('Forwarder', function () { ); }); - it('Should be to able to flush ERC721 tokens when forwarder is owner', async function () { + it('Should be able to flush ERC721 tokens when forwarder is owner', async function () { tokenId = tokenId + 1; const owner = accounts[4]; await token721.mint(owner, tokenId); @@ -300,7 +359,7 @@ describe('Forwarder', function () { expect(await token721.ownerOf(tokenId)).to.be.equal(baseAddress); }); - it('Should be to able to flush ERC721 tokens when forwarder is approved', async function () { + it('Should be able to flush ERC721 tokens when forwarder is approved', async function () { tokenId = tokenId + 1; const owner = accounts[5]; await token721.mint(owner, tokenId); @@ -389,7 +448,7 @@ describe('Forwarder', function () { forwarderBalancePostFlush.toNumber().should.equal(0); }); - it('should fail to flush erc1155 tokens when caller is not parent', async () => { + it('should fail to flush erc1155 tokens when caller is not allowed address', async () => { const owner = baseAddress; const token1155 = await ERC1155.new({ from: owner }); @@ -398,12 +457,12 @@ describe('Forwarder', function () { noAutoFlushForwarder.flushERC1155Tokens( token1155.address, erc1155TokenId, - { from: accounts[2] } + { from: accounts[3] } ) ); }); - it('should batch flush erc1155 tokens back to parent address when caller is parent', async () => { + it('should batch flush erc1155 tokens back to parent address when caller is allowed address', async () => { const erc1155TokenIds = [1, 2, 3]; const amounts = [10, 20, 30]; @@ -438,7 +497,7 @@ describe('Forwarder', function () { } }); - it('should fail to batch flush erc1155 tokens when caller is not parent', async () => { + it('should fail to batch flush erc1155 tokens when caller is not allowed address', async () => { const owner = baseAddress; const token1155 = await ERC1155.new({ from: owner }); @@ -447,7 +506,7 @@ describe('Forwarder', function () { token1155.address, [], { - from: accounts[2] + from: accounts[3] } ) ); @@ -601,7 +660,8 @@ describe('Forwarder', function () { Object.entries(INTERFACE_IDS).map(([eipInterface, interfaceId]) => { it(`should support ${eipInterface}`, async function () { const baseAddress = accounts[3]; - const forwarder = await createForwarder(baseAddress, baseAddress); + const feeAddress = accounts[4]; + const forwarder = await createForwarder(baseAddress, baseAddress, feeAddress); const supportsInterface = await forwarder.supportsInterface( interfaceId @@ -625,7 +685,8 @@ describe('Forwarder', function () { reentryForwarderInstance = await ReentryForwarder.new(); forwarder = await createForwarder( accounts[0], - reentryForwarderInstance.address + reentryForwarderInstance.address, + accounts[1] ); }); beforeEach(async function () {