Skip to content

Commit

Permalink
feat(contracts): add new forwarder contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
gianchandania committed Oct 8, 2023
1 parent adf2ea2 commit 26c90ef
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 52 deletions.
2 changes: 1 addition & 1 deletion contracts/UpdatedForwarder.sol
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
2 changes: 1 addition & 1 deletion contracts/UpdatedForwarderFactory.sol
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
161 changes: 111 additions & 50 deletions test/updatedForwarder.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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));
};
Expand All @@ -41,15 +82,15 @@ 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');
accounts = await web3.eth.getAccounts();
});

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');

Expand All @@ -74,47 +115,49 @@ 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
});

// 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');

Expand All @@ -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 });
Expand All @@ -149,39 +202,43 @@ 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] })
);
});

describe('NFT Support', function () {
let token721;
let tokenId = 0;
let baseAddress;
let feeAddress;
let autoFlushForwarder;
let noAutoFlushForwarder;

Expand All @@ -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 () {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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 });

Expand All @@ -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];

Expand Down Expand Up @@ -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 });

Expand All @@ -447,7 +506,7 @@ describe('Forwarder', function () {
token1155.address,
[],
{
from: accounts[2]
from: accounts[3]
}
)
);
Expand Down Expand Up @@ -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
Expand All @@ -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 () {
Expand Down

0 comments on commit 26c90ef

Please sign in to comment.