From aa20eaac47262ce704308d50216c4db6f4b7f187 Mon Sep 17 00:00:00 2001 From: Michael Zhu Date: Thu, 9 Jun 2022 12:06:21 -0700 Subject: [PATCH] Add FeatureStorage test --- .../1-architecture/Architecture.t.sol | 53 +------ .../2-features/FeatureStorage.t.sol | 137 ++++++++++++++++++ .../protocol-academy/utils/DeployZeroEx.sol | 70 +++++++++ 3 files changed, 209 insertions(+), 51 deletions(-) create mode 100644 contracts/zero-ex/contracts/test/foundry/protocol-academy/2-features/FeatureStorage.t.sol create mode 100644 contracts/zero-ex/contracts/test/foundry/protocol-academy/utils/DeployZeroEx.sol diff --git a/contracts/zero-ex/contracts/test/foundry/protocol-academy/1-architecture/Architecture.t.sol b/contracts/zero-ex/contracts/test/foundry/protocol-academy/1-architecture/Architecture.t.sol index d0cdb79d3d..ed91a8b117 100644 --- a/contracts/zero-ex/contracts/test/foundry/protocol-academy/1-architecture/Architecture.t.sol +++ b/contracts/zero-ex/contracts/test/foundry/protocol-academy/1-architecture/Architecture.t.sol @@ -20,22 +20,14 @@ pragma solidity ^0.6; pragma experimental ABIEncoderV2; -import "forge-std/Test.sol"; - -import "src/IZeroEx.sol"; -import "src/ZeroEx.sol"; -import "src/migrations/InitialMigration.sol"; import "src/features/OtcOrdersFeature.sol"; -import "src/features/OwnableFeature.sol"; -import "src/features/SimpleFunctionRegistryFeature.sol"; import "src/features/TransformERC20Feature.sol"; import "src/features/UniswapFeature.sol"; import "src/features/UniswapV3Feature.sol"; import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol"; +import "../utils/DeployZeroEx.sol"; -contract Architecture is Test { - ZeroEx public ZERO_EX = ZeroEx(0xDef1C0ded9bec7F1a1670819833240f027b25EfF); - IZeroEx public IZERO_EX = IZeroEx(0xDef1C0ded9bec7F1a1670819833240f027b25EfF); +contract Architecture is DeployZeroEx { function testFeatureDeployment() public @@ -71,45 +63,4 @@ contract Architecture is Test { // Or for extra credit, the TransformERC20Feature emit log_named_address("transformERC20 implementation", ZERO_EX.getFunctionImplementation(TransformERC20Feature.transformERC20.selector)); } - - - function deployZeroEx() - internal - returns (address) - { - // HERE BE DRAGONS, feel free to ignore this for now - // We want to deploy the ZeroEx contract, at 0xDef1C0ded9bec7F1a1670819833240f027b25EfF - - // We use a special mechanism to deploy the ZeroEx contract. - InitialMigration initialMigration = InitialMigration(deployCode( - "foundry-artifacts/InitialMigration.sol/InitialMigration.json", - abi.encode(address(this)) - )); - // Must occur from this address as the first transaction - hoax(0xe750ad66DE350F8110E305fb78Ec6A9f594445E3); - // Special Deployer code - bytes memory deployerBytecode = hex"608060405234801561001057600080fd5b506040516103da3803806103da83398101604081905261002f91610077565b8060405161003c9061006a565b610046919061011f565b604051809103906000f080158015610062573d6000803e3d6000fd5b505050610198565b6101f5806101e583390190565b600060208284031215610088578081fd5b81516001600160401b038082111561009e578283fd5b818401915084601f8301126100b1578283fd5b8151818111156100c3576100c3610182565b604051601f8201601f19908116603f011681019083821181831017156100eb576100eb610182565b81604052828152876020848701011115610103578586fd5b610114836020830160208801610152565b979650505050505050565b600060208252825180602084015261013e816040850160208701610152565b601f01601f19169190910160400192915050565b60005b8381101561016d578181015183820152602001610155565b8381111561017c576000848401525b50505050565b634e487b7160e01b600052604160045260246000fd5b603f806101a66000396000f3fe6080604052600080fdfea26469706673582212201bd8b1a777b100d67435ca4bb0b2fdccb13a2c2dde019b227bb553ff9a95bd4464736f6c63430008020033608060405234801561001057600080fd5b506040516101f53803806101f583398101604081905261002f916100c9565b60008151602083016000f090506001600160a01b0381166100865760405162461bcd60e51b815260206004820152600d60248201526c1111541313d657d19052531151609a1b604482015260640160405180910390fd5b6040516001600160a01b03821681527ff40fcec21964ffb566044d083b4073f29f7f7929110ea19e1b3ebe375d89055e9060200160405180910390a150506101a8565b600060208083850312156100db578182fd5b82516001600160401b03808211156100f1578384fd5b818501915085601f830112610104578384fd5b81518181111561011657610116610192565b604051601f8201601f19908116603f0116810190838211818310171561013e5761013e610192565b816040528281528886848701011115610155578687fd5b8693505b828410156101765784840186015181850187015292850192610159565b8284111561018657868684830101525b98975050505050505050565b634e487b7160e01b600052604160045260246000fd5b603f806101b66000396000f3fe6080604052600080fdfea2646970667358221220fbca036a163ed7f008cefa7c834d98d25109a456a051d41d9c89d55d7185d12b64736f6c63430008020033"; - // Grab the bytecode of the ZeroEx artifact - bytes memory zeroExBytecode = vm.getCode("foundry-artifacts/ZeroEx.sol/ZeroEx.json"); - // Append the required ZeroEx constructor arguments (address bootstrapper) - bytes memory zeroExDeploycode = abi.encodePacked(zeroExBytecode, abi.encode(initialMigration)); - // Append the required deployer code constructor arguments (bytes initCode) - bytes memory deployerDeploycode = abi.encodePacked(deployerBytecode, abi.encode(zeroExDeploycode)); - // The address is technically emitted in an event, but we know we did it correctly - //│ │ ├─ emit topic 0: 0xf40fcec21964ffb566044d083b4073f29f7f7929110ea19e1b3ebe375d89055e - //│ │ │ data: 0x000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff - assembly { - pop(create(0, add(deployerDeploycode, 0x20), mload(deployerDeploycode))) - } - - initialMigration.initializeZeroEx( - payable(address(this)), - ZERO_EX, - InitialMigration.BootstrapFeatures({ registry: new SimpleFunctionRegistryFeature(), ownable: new OwnableFeature()}) - ); - // Register other features - // SimpleFunctionRegistryFeature - // OwnableFeature - return address(ZERO_EX); - } } diff --git a/contracts/zero-ex/contracts/test/foundry/protocol-academy/2-features/FeatureStorage.t.sol b/contracts/zero-ex/contracts/test/foundry/protocol-academy/2-features/FeatureStorage.t.sol new file mode 100644 index 0000000000..71feb30f5c --- /dev/null +++ b/contracts/zero-ex/contracts/test/foundry/protocol-academy/2-features/FeatureStorage.t.sol @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2022 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.6; +pragma experimental ABIEncoderV2; + +import "src/features/OtcOrdersFeature.sol"; +import "src/features/TransformERC20Feature.sol"; +import "src/features/UniswapFeature.sol"; +import "src/features/UniswapV3Feature.sol"; +import "src/fixins/FixinCommon.sol"; +import "src/migrations/LibMigrate.sol"; +import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol"; +import "../utils/DeployZeroEx.sol"; + +contract FibonacciFeature is FixinCommon { + uint256 private prevFib; + uint256 private fib; + + function migrate() + external + returns (bytes4 success) + { + _registerFeatureFunction(this.stepFibonacci.selector); + _registerFeatureFunction(this.currentFibonacci.selector); + return LibMigrate.MIGRATE_SUCCESS; + } + + function stepFibonacci() external { + if (prevFib == 0 && fib == 0) { + fib = 1; + return; + } else { + uint256 nextFib = prevFib + fib; + prevFib = fib; + fib = nextFib; + } + } + + function currentFibonacci() external view returns (uint256) { + return fib; + } +} + +contract VoteFeature is FixinCommon { + uint256 private good; + uint256 private bad; + mapping (address => bool) hasVoted; + + function migrate() + external + returns (bytes4 success) + { + _registerFeatureFunction(this.pineappleOnPizzaIsGood.selector); + _registerFeatureFunction(this.pineappleOnPizzaIsBad.selector); + _registerFeatureFunction(this.currentVotes.selector); + return LibMigrate.MIGRATE_SUCCESS; + } + + function pineappleOnPizzaIsGood() external { + require(!hasVoted[msg.sender]); + hasVoted[msg.sender] = true; + good++; + } + + function pineappleOnPizzaIsBad() external { + require(!hasVoted[msg.sender]); + hasVoted[msg.sender] = true; + bad++; + } + + function currentVotes() external view returns (uint256, uint256) { + return (good, bad); + } +} + +contract FeatureStorage is DeployZeroEx { + + function setUp() public { + deployZeroEx(); + FibonacciFeature fibonacciFeature = new FibonacciFeature(); + emit log_named_address("FibonacciFeature deployed at", address(fibonacciFeature)); + IZERO_EX.migrate(address(fibonacciFeature), abi.encodePacked(fibonacciFeature.migrate.selector), address(this)); + emit log_named_address("stepFibonacci implementation", ZERO_EX.getFunctionImplementation(FibonacciFeature.stepFibonacci.selector)); + + VoteFeature voteFeature = new VoteFeature(); + emit log_named_address("VoteFeature deployed at", address(voteFeature)); + IZERO_EX.migrate(address(voteFeature), abi.encodePacked(voteFeature.migrate.selector), address(this)); + } + + function testFeatureStorage() + public + { + for (uint256 i = 0; i < 10; i++) { + emit log_named_uint("fib", FibonacciFeature(address(ZERO_EX)).currentFibonacci()); + FibonacciFeature(address(ZERO_EX)).stepFibonacci(); + } + + for (uint256 i = 0; i < 10; i++) { + address voter = _pseudoRandomAddress(i); + bool likesPineappleOnPizza = (uint160(voter) % 2) == 1; + hoax(voter); + likesPineappleOnPizza + ? VoteFeature(address(ZERO_EX)).pineappleOnPizzaIsGood() + : VoteFeature(address(ZERO_EX)).pineappleOnPizzaIsBad(); + } + (uint256 good, uint256 bad) = VoteFeature(address(ZERO_EX)).currentVotes(); + + emit log_named_uint("good votes", good); + emit log_named_uint("bad votes", bad); + emit log_named_uint("fib", FibonacciFeature(address(ZERO_EX)).currentFibonacci()); + + // TODO: Fix the Fibonacci and Vote features so that their respective storage + // variables do not collide. Create LibFibonacciStorage and LibVoteStorage + // to do so. + } + + function _pseudoRandomAddress(uint256 seed) private pure returns (address) { + return address(uint160(uint256(keccak256(abi.encodePacked(seed))))); + } +} diff --git a/contracts/zero-ex/contracts/test/foundry/protocol-academy/utils/DeployZeroEx.sol b/contracts/zero-ex/contracts/test/foundry/protocol-academy/utils/DeployZeroEx.sol new file mode 100644 index 0000000000..e4ae1d0d28 --- /dev/null +++ b/contracts/zero-ex/contracts/test/foundry/protocol-academy/utils/DeployZeroEx.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2022 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.6; +pragma experimental ABIEncoderV2; + +import "forge-std/Test.sol"; + +import "src/IZeroEx.sol"; +import "src/ZeroEx.sol"; +import "src/migrations/InitialMigration.sol"; +import "src/features/OwnableFeature.sol"; +import "src/features/SimpleFunctionRegistryFeature.sol"; + + +contract DeployZeroEx is Test { + ZeroEx public ZERO_EX = ZeroEx(0xDef1C0ded9bec7F1a1670819833240f027b25EfF); + IZeroEx public IZERO_EX = IZeroEx(0xDef1C0ded9bec7F1a1670819833240f027b25EfF); + + function deployZeroEx() + internal + { + // HERE BE DRAGONS, feel free to ignore this for now + // We want to deploy the ZeroEx contract, at 0xDef1C0ded9bec7F1a1670819833240f027b25EfF + + // We use a special mechanism to deploy the ZeroEx contract. + InitialMigration initialMigration = InitialMigration(deployCode( + "foundry-artifacts/InitialMigration.sol/InitialMigration.json", + abi.encode(address(this)) + )); + // Must occur from this address as the first transaction + hoax(0xe750ad66DE350F8110E305fb78Ec6A9f594445E3); + // Special Deployer code + bytes memory deployerBytecode = hex"608060405234801561001057600080fd5b506040516103da3803806103da83398101604081905261002f91610077565b8060405161003c9061006a565b610046919061011f565b604051809103906000f080158015610062573d6000803e3d6000fd5b505050610198565b6101f5806101e583390190565b600060208284031215610088578081fd5b81516001600160401b038082111561009e578283fd5b818401915084601f8301126100b1578283fd5b8151818111156100c3576100c3610182565b604051601f8201601f19908116603f011681019083821181831017156100eb576100eb610182565b81604052828152876020848701011115610103578586fd5b610114836020830160208801610152565b979650505050505050565b600060208252825180602084015261013e816040850160208701610152565b601f01601f19169190910160400192915050565b60005b8381101561016d578181015183820152602001610155565b8381111561017c576000848401525b50505050565b634e487b7160e01b600052604160045260246000fd5b603f806101a66000396000f3fe6080604052600080fdfea26469706673582212201bd8b1a777b100d67435ca4bb0b2fdccb13a2c2dde019b227bb553ff9a95bd4464736f6c63430008020033608060405234801561001057600080fd5b506040516101f53803806101f583398101604081905261002f916100c9565b60008151602083016000f090506001600160a01b0381166100865760405162461bcd60e51b815260206004820152600d60248201526c1111541313d657d19052531151609a1b604482015260640160405180910390fd5b6040516001600160a01b03821681527ff40fcec21964ffb566044d083b4073f29f7f7929110ea19e1b3ebe375d89055e9060200160405180910390a150506101a8565b600060208083850312156100db578182fd5b82516001600160401b03808211156100f1578384fd5b818501915085601f830112610104578384fd5b81518181111561011657610116610192565b604051601f8201601f19908116603f0116810190838211818310171561013e5761013e610192565b816040528281528886848701011115610155578687fd5b8693505b828410156101765784840186015181850187015292850192610159565b8284111561018657868684830101525b98975050505050505050565b634e487b7160e01b600052604160045260246000fd5b603f806101b66000396000f3fe6080604052600080fdfea2646970667358221220fbca036a163ed7f008cefa7c834d98d25109a456a051d41d9c89d55d7185d12b64736f6c63430008020033"; + // Grab the bytecode of the ZeroEx artifact + bytes memory zeroExBytecode = vm.getCode("foundry-artifacts/ZeroEx.sol/ZeroEx.json"); + // Append the required ZeroEx constructor arguments (address bootstrapper) + bytes memory zeroExDeploycode = abi.encodePacked(zeroExBytecode, abi.encode(initialMigration)); + // Append the required deployer code constructor arguments (bytes initCode) + bytes memory deployerDeploycode = abi.encodePacked(deployerBytecode, abi.encode(zeroExDeploycode)); + // The address is technically emitted in an event, but we know we did it correctly + //│ │ ├─ emit topic 0: 0xf40fcec21964ffb566044d083b4073f29f7f7929110ea19e1b3ebe375d89055e + //│ │ │ data: 0x000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff + assembly { + pop(create(0, add(deployerDeploycode, 0x20), mload(deployerDeploycode))) + } + + initialMigration.initializeZeroEx( + payable(address(this)), + ZERO_EX, + InitialMigration.BootstrapFeatures({ registry: new SimpleFunctionRegistryFeature(), ownable: new OwnableFeature()}) + ); + } +}