From 90b6707ba01092f31a47c432a55ef12df49bc3f9 Mon Sep 17 00:00:00 2001 From: Logan Nguyen Date: Tue, 12 Dec 2023 20:29:34 -0600 Subject: [PATCH] feat: added example to showcase OZ ERC1967Upgrade (#613) (#615) * feat: added Voting solidity contracts Signed-off-by: Logan Nguyen * test: added tests for erc1967 upgradable Signed-off-by: Logan Nguyen * update: added extra line at EOF & removed dbg file Signed-off-by: Logan Nguyen * update: added bytes argument to overriden upgradeToAndCall Signed-off-by: Logan Nguyen * fix: fixed unit test Signed-off-by: Logan Nguyen --------- Signed-off-by: Logan Nguyen --- .../VoteProxy.sol/VoteProxy.json | 193 +++++++++++++++ .../oz/ERC1967Upgrade/VoteV1.sol/VoteV1.json | 103 ++++++++ .../oz/ERC1967Upgrade/VoteV2.sol/VoteV2.json | 122 ++++++++++ .../solidity/oz/ERC1967Upgrade/VoteProxy.sol | 97 ++++++++ .../solidity/oz/ERC1967Upgrade/VoteV1.sol | 87 +++++++ .../solidity/oz/ERC1967Upgrade/VoteV2.sol | 58 +++++ test/solidity/oz/ERC1967Upgrade/Vote.js | 219 ++++++++++++++++++ 7 files changed, 879 insertions(+) create mode 100644 artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteProxy.sol/VoteProxy.json create mode 100644 artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteV1.sol/VoteV1.json create mode 100644 artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteV2.sol/VoteV2.json create mode 100644 contracts/solidity/oz/ERC1967Upgrade/VoteProxy.sol create mode 100644 contracts/solidity/oz/ERC1967Upgrade/VoteV1.sol create mode 100644 contracts/solidity/oz/ERC1967Upgrade/VoteV2.sol create mode 100644 test/solidity/oz/ERC1967Upgrade/Vote.js diff --git a/artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteProxy.sol/VoteProxy.json b/artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteProxy.sol/VoteProxy.json new file mode 100644 index 000000000..674f7d7f8 --- /dev/null +++ b/artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteProxy.sol/VoteProxy.json @@ -0,0 +1,193 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "VoteProxy", + "sourceName": "contracts/solidity/oz/ERC1967Upgrade/VoteProxy.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "implementationContract", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "ERC1967InvalidAdmin", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInnerCall", + "type": "error" + }, + { + "inputs": [], + "name": "Unauthorized_Caller", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "changeAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAdminSlot", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getImplementationSlot", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162000a8238038062000a82833981016040819052620000349162000330565b604080516020810190915260008152819062000051828262000066565b506200005f905033620000cc565b506200038c565b62000071826200013e565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a2805115620000be57620000b98282620001be565b505050565b620000c86200023b565b5050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6200010e60008051602062000a62833981519152546001600160a01b031690565b604080516001600160a01b03928316815291841660208301520160405180910390a16200013b816200025d565b50565b806001600160a01b03163b6000036200017a57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b6060600080846001600160a01b031684604051620001dd91906200035b565b600060405180830381855af49150503d80600081146200021a576040519150601f19603f3d011682016040523d82523d6000602084013e6200021f565b606091505b50909250905062000232858383620002a0565b95945050505050565b34156200025b5760405163b398979f60e01b815260040160405180910390fd5b565b6001600160a01b0381166200028957604051633173bdd160e11b81526000600482015260240162000171565b8060008051602062000a628339815191526200019d565b606082620002b957620002b38262000306565b620002ff565b8151158015620002d157506001600160a01b0384163b155b15620002fc57604051639996b31560e01b81526001600160a01b038516600482015260240162000171565b50805b9392505050565b805115620003175780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6000602082840312156200034357600080fd5b81516001600160a01b0381168114620002ff57600080fd5b6000825160005b818110156200037e576020818601810151858301520162000362565b506000920191825250919050565b6106c6806200039c6000396000f3fe6080604052600436106100695760003560e01c80637a34c821116100435780637a34c821146100e15780638f2839701461011e5780639ad5c59a1461013e57610070565b80634f1ef2861461007a5780635c60da1b1461009a578063643d430c146100cc57610070565b3661007057005b610078610171565b005b34801561008657600080fd5b50610078610095366004610584565b610183565b3480156100a657600080fd5b506100af6101cc565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100d857600080fd5b506100af6101db565b3480156100ed57600080fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b6040519081526020016100c3565b34801561012a57600080fd5b50610078610139366004610646565b6101e5565b34801561014a57600080fd5b507fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103610110565b61018161017c6101cc565b61022c565b565b3361018c610250565b6001600160a01b0316816001600160a01b0316146101bd5760405163235c17e160e11b815260040160405180910390fd5b6101c78383610283565b505050565b60006101d66102d9565b905090565b60006101d6610250565b336101ee610250565b6001600160a01b0316816001600160a01b03161461021f5760405163235c17e160e11b815260040160405180910390fd5b61022882610301565b5050565b3660008037600080366000845af43d6000803e80801561024b573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b61028c82610358565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a28051156102d1576101c782826103e4565b61022861045a565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610274565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61032a610250565b604080516001600160a01b03928316815291841660208301520160405180910390a161035581610479565b50565b806001600160a01b03163b60000361039357604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b039290921691909117905550565b6060600080846001600160a01b0316846040516104019190610661565b600060405180830381855af49150503d806000811461043c576040519150601f19603f3d011682016040523d82523d6000602084013e610441565b606091505b50915091506104518583836104ca565b95945050505050565b34156101815760405163b398979f60e01b815260040160405180910390fd5b6001600160a01b0381166104a357604051633173bdd160e11b81526000600482015260240161038a565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036103b6565b6060826104df576104da82610529565b610522565b81511580156104f657506001600160a01b0384163b155b1561051f57604051639996b31560e01b81526001600160a01b038516600482015260240161038a565b50805b9392505050565b8051156105395780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b038116811461056957600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561059757600080fd5b6105a083610552565b9150602083013567ffffffffffffffff808211156105bd57600080fd5b818501915085601f8301126105d157600080fd5b8135818111156105e3576105e361056e565b604051601f8201601f19908116603f0116810190838211818310171561060b5761060b61056e565b8160405282815288602084870101111561062457600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60006020828403121561065857600080fd5b61052282610552565b6000825160005b818110156106825760208186018101518583015201610668565b50600092019182525091905056fea26469706673582212202aaf3a762083dd7fa6a6a17a283e93b780d61d425d8a7733156d3caf1dd311ea64736f6c63430008140033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103", + "deployedBytecode": "0x6080604052600436106100695760003560e01c80637a34c821116100435780637a34c821146100e15780638f2839701461011e5780639ad5c59a1461013e57610070565b80634f1ef2861461007a5780635c60da1b1461009a578063643d430c146100cc57610070565b3661007057005b610078610171565b005b34801561008657600080fd5b50610078610095366004610584565b610183565b3480156100a657600080fd5b506100af6101cc565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100d857600080fd5b506100af6101db565b3480156100ed57600080fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b6040519081526020016100c3565b34801561012a57600080fd5b50610078610139366004610646565b6101e5565b34801561014a57600080fd5b507fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103610110565b61018161017c6101cc565b61022c565b565b3361018c610250565b6001600160a01b0316816001600160a01b0316146101bd5760405163235c17e160e11b815260040160405180910390fd5b6101c78383610283565b505050565b60006101d66102d9565b905090565b60006101d6610250565b336101ee610250565b6001600160a01b0316816001600160a01b03161461021f5760405163235c17e160e11b815260040160405180910390fd5b61022882610301565b5050565b3660008037600080366000845af43d6000803e80801561024b573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b61028c82610358565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a28051156102d1576101c782826103e4565b61022861045a565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610274565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61032a610250565b604080516001600160a01b03928316815291841660208301520160405180910390a161035581610479565b50565b806001600160a01b03163b60000361039357604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b039290921691909117905550565b6060600080846001600160a01b0316846040516104019190610661565b600060405180830381855af49150503d806000811461043c576040519150601f19603f3d011682016040523d82523d6000602084013e610441565b606091505b50915091506104518583836104ca565b95945050505050565b34156101815760405163b398979f60e01b815260040160405180910390fd5b6001600160a01b0381166104a357604051633173bdd160e11b81526000600482015260240161038a565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036103b6565b6060826104df576104da82610529565b610522565b81511580156104f657506001600160a01b0384163b155b1561051f57604051639996b31560e01b81526001600160a01b038516600482015260240161038a565b50805b9392505050565b8051156105395780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b038116811461056957600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561059757600080fd5b6105a083610552565b9150602083013567ffffffffffffffff808211156105bd57600080fd5b818501915085601f8301126105d157600080fd5b8135818111156105e3576105e361056e565b604051601f8201601f19908116603f0116810190838211818310171561060b5761060b61056e565b8160405282815288602084870101111561062457600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60006020828403121561065857600080fd5b61052282610552565b6000825160005b818110156106825760208186018101518583015201610668565b50600092019182525091905056fea26469706673582212202aaf3a762083dd7fa6a6a17a283e93b780d61d425d8a7733156d3caf1dd311ea64736f6c63430008140033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteV1.sol/VoteV1.json b/artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteV1.sol/VoteV1.json new file mode 100644 index 000000000..450e7a06f --- /dev/null +++ b/artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteV1.sol/VoteV1.json @@ -0,0 +1,103 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "VoteV1", + "sourceName": "contracts/solidity/oz/ERC1967Upgrade/VoteV1.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "Voter_Has_Already_Voted", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "voter", + "type": "address" + } + ], + "name": "voted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "voters", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b5060016000556103a5806100256000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063350580ea1461005c57806354fd4d501461007a578063632a9a521461008b5780638129fc1c14610095578063aec2ccae1461009d575b600080fd5b6100646100d9565b60405161007191906102f2565b60405180910390f35b600054604051908152602001610071565b61009361013b565b005b6100936101d3565b6100c96100ab36600461033f565b6001600160a01b031660009081526002602052604090205460ff1690565b6040519015158152602001610071565b6060600180548060200260200160405190810160405280929190818152602001828054801561013157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610113575b5050505050905090565b3360008181526002602052604090205460ff161561016c57604051633b1dbd5760e11b815260040160405180910390fd5b506001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf601805473ffffffffffffffffffffffffffffffffffffffff1916339081179091556000908152600260205260409020805460ff19169091179055565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff1660008115801561021e5750825b905060008267ffffffffffffffff16600114801561023b5750303b155b905081158015610249575080155b156102675760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561029b57845468ff00000000000000001916680100000000000000001785555b600160005583156102eb57845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050565b6020808252825182820181905260009190848201906040850190845b818110156103335783516001600160a01b03168352928401929184019160010161030e565b50909695505050505050565b60006020828403121561035157600080fd5b81356001600160a01b038116811461036857600080fd5b939250505056fea26469706673582212205146ed303b9ede0c0d8554ecaa9f3f408bf1f0ec621c8c59edc7f2847f9bbc6364736f6c63430008140033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100575760003560e01c8063350580ea1461005c57806354fd4d501461007a578063632a9a521461008b5780638129fc1c14610095578063aec2ccae1461009d575b600080fd5b6100646100d9565b60405161007191906102f2565b60405180910390f35b600054604051908152602001610071565b61009361013b565b005b6100936101d3565b6100c96100ab36600461033f565b6001600160a01b031660009081526002602052604090205460ff1690565b6040519015158152602001610071565b6060600180548060200260200160405190810160405280929190818152602001828054801561013157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610113575b5050505050905090565b3360008181526002602052604090205460ff161561016c57604051633b1dbd5760e11b815260040160405180910390fd5b506001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf601805473ffffffffffffffffffffffffffffffffffffffff1916339081179091556000908152600260205260409020805460ff19169091179055565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff1660008115801561021e5750825b905060008267ffffffffffffffff16600114801561023b5750303b155b905081158015610249575080155b156102675760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561029b57845468ff00000000000000001916680100000000000000001785555b600160005583156102eb57845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050565b6020808252825182820181905260009190848201906040850190845b818110156103335783516001600160a01b03168352928401929184019160010161030e565b50909695505050505050565b60006020828403121561035157600080fd5b81356001600160a01b038116811461036857600080fd5b939250505056fea26469706673582212205146ed303b9ede0c0d8554ecaa9f3f408bf1f0ec621c8c59edc7f2847f9bbc6364736f6c63430008140033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteV2.sol/VoteV2.json b/artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteV2.sol/VoteV2.json new file mode 100644 index 000000000..2ff65e58c --- /dev/null +++ b/artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteV2.sol/VoteV2.json @@ -0,0 +1,122 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "VoteV2", + "sourceName": "contracts/solidity/oz/ERC1967Upgrade/VoteV2.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "Voter_Has_Already_Voted", + "type": "error" + }, + { + "inputs": [], + "name": "Voter_Has_Not_Voted", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "initializeV2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "voter", + "type": "address" + } + ], + "name": "voted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "voters", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawVote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b506002600055610697806100256000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c8063632a9a521161005b578063632a9a52146100bb5780638129fc1c146100c3578063aec2ccae146100cb578063d83eb2311461010757600080fd5b8063350580ea1461008257806354fd4d50146100a05780635cd8a76b146100b1575b600080fd5b61008a61010f565b604051610097919061055d565b60405180910390f35b600054604051908152602001610097565b6100b9610171565b005b6100b9610249565b6100b96102d4565b6100f76100d93660046105aa565b6001600160a01b031660009081526002602052604090205460ff1690565b6040519015158152602001610097565b6100b96103e9565b6060600180548060200260200160405190810160405280929190818152602001828054801561016757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610149575b5050505050905090565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805460029190600160401b900460ff16806101bb5750805467ffffffffffffffff808416911610155b156101d95760405163f92ee8a960e01b815260040160405180910390fd5b805468ffffffffffffffffff191667ffffffffffffffff8316908117600160401b1782556002600055815468ff0000000000000000191682556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050565b3360008181526002602052604090205460ff161561027a57604051633b1dbd5760e11b815260040160405180910390fd5b506001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b031916339081179091556000908152600260205260409020805460ff19169091179055565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff1660008115801561031a5750825b905060008267ffffffffffffffff1660011480156103375750303b155b905081158015610345575080155b156103635760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561039257845468ff00000000000000001916600160401b1785555b600160005583156103e257845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050565b3360008181526002602052604090205460ff1661041957604051630919808b60e01b815260040160405180910390fd5b60005b60015481101561050f57336001600160a01b031660018281548110610443576104436105da565b6000918252602090912001546001600160a01b0316036104fd57805b6001805461046d9190610606565b8110156104f7576001610480828261061f565b81548110610490576104906105da565b600091825260209091200154600180546001600160a01b0390921691839081106104bc576104bc6105da565b600091825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055806104ef81610632565b91505061045f565b5061050f565b8061050781610632565b91505061041c565b50336000908152600260205260409020805460ff1916905560018054806105385761053861064b565b600082815260209020810160001990810180546001600160a01b031916905501905550565b6020808252825182820181905260009190848201906040850190845b8181101561059e5783516001600160a01b031683529284019291840191600101610579565b50909695505050505050565b6000602082840312156105bc57600080fd5b81356001600160a01b03811681146105d357600080fd5b9392505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115610619576106196105f0565b92915050565b80820180821115610619576106196105f0565b600060018201610644576106446105f0565b5060010190565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220630d3e90f82dfc720dfcd547ec09d18171bd728b0d65ce0d415f17af7aee6ebf64736f6c63430008140033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061007d5760003560e01c8063632a9a521161005b578063632a9a52146100bb5780638129fc1c146100c3578063aec2ccae146100cb578063d83eb2311461010757600080fd5b8063350580ea1461008257806354fd4d50146100a05780635cd8a76b146100b1575b600080fd5b61008a61010f565b604051610097919061055d565b60405180910390f35b600054604051908152602001610097565b6100b9610171565b005b6100b9610249565b6100b96102d4565b6100f76100d93660046105aa565b6001600160a01b031660009081526002602052604090205460ff1690565b6040519015158152602001610097565b6100b96103e9565b6060600180548060200260200160405190810160405280929190818152602001828054801561016757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610149575b5050505050905090565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805460029190600160401b900460ff16806101bb5750805467ffffffffffffffff808416911610155b156101d95760405163f92ee8a960e01b815260040160405180910390fd5b805468ffffffffffffffffff191667ffffffffffffffff8316908117600160401b1782556002600055815468ff0000000000000000191682556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050565b3360008181526002602052604090205460ff161561027a57604051633b1dbd5760e11b815260040160405180910390fd5b506001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b031916339081179091556000908152600260205260409020805460ff19169091179055565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff1660008115801561031a5750825b905060008267ffffffffffffffff1660011480156103375750303b155b905081158015610345575080155b156103635760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561039257845468ff00000000000000001916600160401b1785555b600160005583156103e257845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050565b3360008181526002602052604090205460ff1661041957604051630919808b60e01b815260040160405180910390fd5b60005b60015481101561050f57336001600160a01b031660018281548110610443576104436105da565b6000918252602090912001546001600160a01b0316036104fd57805b6001805461046d9190610606565b8110156104f7576001610480828261061f565b81548110610490576104906105da565b600091825260209091200154600180546001600160a01b0390921691839081106104bc576104bc6105da565b600091825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055806104ef81610632565b91505061045f565b5061050f565b8061050781610632565b91505061041c565b50336000908152600260205260409020805460ff1916905560018054806105385761053861064b565b600082815260209020810160001990810180546001600160a01b031916905501905550565b6020808252825182820181905260009190848201906040850190845b8181101561059e5783516001600160a01b031683529284019291840191600101610579565b50909695505050505050565b6000602082840312156105bc57600080fd5b81356001600160a01b03811681146105d357600080fd5b9392505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115610619576106196105f0565b92915050565b80820180821115610619576106196105f0565b600060018201610644576106446105f0565b5060010190565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220630d3e90f82dfc720dfcd547ec09d18171bd728b0d65ce0d415f17af7aee6ebf64736f6c63430008140033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/contracts/solidity/oz/ERC1967Upgrade/VoteProxy.sol b/contracts/solidity/oz/ERC1967Upgrade/VoteProxy.sol new file mode 100644 index 000000000..894d99050 --- /dev/null +++ b/contracts/solidity/oz/ERC1967Upgrade/VoteProxy.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/utils/Context.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; + + +/** + * @dev This contract acts as a Proxy contract for the vote contracts + */ +contract VoteProxy is ERC1967Proxy, Context { + /** + * @dev caller is not authorized + */ + error Unauthorized_Caller(); + + /** + * @dev Initializes the upgradeable ERC1967Proxy with an initial implementation specified by `implementation` and an empty call data. + */ + constructor(address implementationContract) ERC1967Proxy(implementationContract, "") { + ERC1967Utils.changeAdmin(_msgSender()); + } + + /** + * @dev modifier which only allows proxy admin + */ + modifier onlyProxyAdmin(address caller) { + if (caller != ERC1967Utils.getAdmin()) { + revert Unauthorized_Caller(); + } + _; + } + + /** + * @notice required by Solidity + */ + receive() external payable {} + + /** + * @dev Returns the predefined IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc + * + * @notice The IMPLEMENTATION_SLOT is obtained as bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) + */ + function getImplementationSlot() external pure returns (bytes32) { + return ERC1967Utils.IMPLEMENTATION_SLOT; + } + + /** + * @dev Returns the current implementation address. + * + * @notice The internal _implementation() utilizes ERC1967Utils.getImplementation(); + */ + function implementation() external view returns (address) { + return ERC1967Utils.getImplementation(); + } + + /** + * @dev Performs implementation upgrade with additional setup call if data is nonempty. + * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected + * to avoid stuck value in the contract. + * + * Emits an {IERC1967-Upgraded} event. + */ + function upgradeToAndCall(address newImplementation, bytes memory data) external onlyProxyAdmin(_msgSender()){ + ERC1967Utils.upgradeToAndCall(newImplementation, data); + } + + /** + * @dev Returns the predefined ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + * + * @notice The ADMIN_SLOT is obtained as bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1) + */ + function getAdminSlot() external pure returns (bytes32) { + return ERC1967Utils.ADMIN_SLOT; + } + + /** + * @dev returns the current proxy admin + * + * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using + * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. + * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` + */ + function getCurrentAdmin() external view returns (address) { + return ERC1967Utils.getAdmin(); + } + + /** + * @dev Changes the admin of the proxy. + * + * Emits an {IERC1967-AdminChanged} event. + */ + function changeAdmin(address newAdmin) external onlyProxyAdmin(_msgSender()) { + ERC1967Utils.changeAdmin(newAdmin); + } +} diff --git a/contracts/solidity/oz/ERC1967Upgrade/VoteV1.sol b/contracts/solidity/oz/ERC1967Upgrade/VoteV1.sol new file mode 100644 index 000000000..271076a01 --- /dev/null +++ b/contracts/solidity/oz/ERC1967Upgrade/VoteV1.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/utils/Context.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +/** + * @dev This contract is an example of a simple voting system. + */ +contract VoteV1 is Context, Initializable { + /** + * @dev version of the vote system + */ + uint256 _version; + + /** + * @dev a list of voters + */ + address[] _voters; + + /** + * @dev voters have voted + */ + mapping(address => bool) _voted; + + /** + * @dev The caller has already voted + */ + error Voter_Has_Already_Voted(); + + /** + * @dev modifier to only allow callers who have not voted to execute the calling method + * @param voter the voter to check + */ + modifier mustHaveNotVoted(address voter) { + if (voted(voter)) { + revert Voter_Has_Already_Voted(); + } + _; + } + + /** + * @dev Initializes the vote system version 1 + */ + constructor() { + _version = 1; + } + + /** + * @dev Initializes the vote system version 1 + */ + function initialize() external initializer { + _version = 1; + } + + /** + * @dev Add a voter to the vote system + */ + function vote() external mustHaveNotVoted(_msgSender()) { + _voters.push(_msgSender()); + _voted[_msgSender()] = true; + } + + /** + * @dev Returs the list of voters + */ + function voters() external view returns (address[] memory) { + return _voters; + } + + /** + * @dev Checks if a voter has already voted + * @param voter the voter to check + * @return true if the voter has already voted, false otherwise + */ + function voted(address voter) public view returns (bool) { + return _voted[voter]; + } + + /** + * @dev Returns the version of the vote system + */ + function version() external view returns(uint256) { + return _version; + } + +} diff --git a/contracts/solidity/oz/ERC1967Upgrade/VoteV2.sol b/contracts/solidity/oz/ERC1967Upgrade/VoteV2.sol new file mode 100644 index 000000000..8c2fabdd2 --- /dev/null +++ b/contracts/solidity/oz/ERC1967Upgrade/VoteV2.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import './VoteV1.sol'; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +/** + * @dev This contract is an upgraded version of the VoteV1 + */ +contract VoteV2 is Initializable, VoteV1 { + + /** + * @dev The caller has not voted + */ + error Voter_Has_Not_Voted(); + + /** + * @dev modifier to only allow callers who have voted to execute the calling method + * @param voter the voter to check + */ + modifier mustHaveVoted(address voter) { + if (!_voted[voter]) { + revert Voter_Has_Not_Voted(); + } + _; + } + + /** + * @dev Initialize the vote system version 2 + */ + constructor() { + _version = 2; + } + + /** + * @dev Initializes the vote system version 2 + */ + function initializeV2() external reinitializer(2) { + _version = 2; + } + + /** + * @dev Allows callers to withdraw their votes + */ + function withdrawVote() external mustHaveVoted(_msgSender()) { + for (uint256 i = 0; i < _voters.length; i++) { + if (_voters[i] == _msgSender()) { + for (uint256 j = i; j < _voters.length - 1; j++) { + _voters[j] = _voters[j+1]; + } + break; + } + } + _voted[_msgSender()] = false; + _voters.pop(); + } + +} diff --git a/test/solidity/oz/ERC1967Upgrade/Vote.js b/test/solidity/oz/ERC1967Upgrade/Vote.js new file mode 100644 index 000000000..f750d4d66 --- /dev/null +++ b/test/solidity/oz/ERC1967Upgrade/Vote.js @@ -0,0 +1,219 @@ +/*- + * + * Hedera Smart Contracts + * + * Copyright (C) 2023 Hedera Hashgraph, LLC + * + * 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. + * + */ + +const { expect } = require('chai') +const { ethers } = require('hardhat') +const fs = require('fs') +const path = require('path') +const { GAS_LIMIT_1_000_000, CALL_EXCEPTION } = require('../../../constants') +const HederaSmartContractsRootPath = path.resolve( + __dirname, + '..', + '..', + '..', + '..' +) + +const VoteV1Artifact = JSON.parse( + fs.readFileSync( + `${HederaSmartContractsRootPath}/artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteV1.sol/VoteV1.json` + ) +) + +const VoteV2Artifact = JSON.parse( + fs.readFileSync( + `${HederaSmartContractsRootPath}/artifacts/contracts/solidity/oz/ERC1967Upgrade/VoteV2.sol/VoteV2.json` + ) +) + +describe('@ERC1967Upgrade Upgradable Vote Tests', () => { + let admin, voter1, voter2 + let voteV1, voteV2, proxiedVoteV1, proxiedVoteV2, voteProxy + const EMPTY_DATA = '0x' + + before(async () => { + ;[admin, voter1, voter2] = await ethers.getSigners() + + const VoteV1Fac = await ethers.getContractFactory('VoteV1') + voteV1 = await VoteV1Fac.deploy() + + const VoteV2Fac = await ethers.getContractFactory('VoteV2') + voteV2 = await VoteV2Fac.deploy() + + const VoteProxyFac = await ethers.getContractFactory('VoteProxy') + voteProxy = await VoteProxyFac.deploy(voteV1.address) + }) + + describe('Proxy Contract tests', () => { + it('Should deploy vote proxy contract with the with voteV1 being the current logic contract', async () => { + expect(await voteProxy.implementation()).to.eq(voteV1.address) + }) + + it('Should upgrade proxy vote to point to voteV2', async () => { + const tx = await voteProxy.upgradeToAndCall(voteV2.address, EMPTY_DATA) + const receipt = await tx.wait() + const event = receipt.events.find((e) => e.event === 'Upgraded') + + expect(event.args.implementation).to.eq(voteV2.address) + expect(await voteProxy.implementation()).to.eq(voteV2.address) + }) + + it('Should be able to get the predefined ERC1967 IMPLEMENTATION_SLOT', async () => { + // @logic ERC1967.IMPLEMENTATION_SLOT is obtained as bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) + + // keccak256('eip1967.proxy.implementation') + const eip1967ImplByte32 = ethers.utils.solidityKeccak256( + ['string'], + ['eip1967.proxy.implementation'] + ) + + // uint256(keccak256('eip1967.proxy.implementation')) - 1 + const eip1967ImplUint256 = ethers.BigNumber.from(eip1967ImplByte32).sub(1) + + // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) + const expectedImplementationSlot = ethers.utils.hexZeroPad( + eip1967ImplUint256.toHexString(), + 32 + ) + + expect(await voteProxy.getImplementationSlot()).to.eq( + expectedImplementationSlot + ) + }) + + it('Should deploy vote proxy contract with a new proxy admin', async () => { + expect(await voteProxy.getCurrentAdmin()).to.eq(await admin.getAddress()) + }) + + it('Should be able to get the predefined ERC1967 ADMIN_SLOT', async () => { + // @logic ERC1967.ADMIN_SLOT is obtained as bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1) + + // keccak256('eip1967.proxy.admin') + const eip1967ImplByte32 = ethers.utils.solidityKeccak256( + ['string'], + ['eip1967.proxy.admin'] + ) + + // uint256(keccak256('eip1967.proxy.admin')) - 1 + const eip1967AdminUint256 = + ethers.BigNumber.from(eip1967ImplByte32).sub(1) + + // bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1) + const expectedAdminSlot = ethers.utils.hexZeroPad( + eip1967AdminUint256.toHexString(), + 32 + ) + + expect(await voteProxy.getAdminSlot()).to.eq(expectedAdminSlot) + }) + + it('Should be able to change the current proxy admin to a new address', async () => { + const tx = await voteProxy.changeAdmin(await voter1.getAddress()) + const receipt = await tx.wait() + + const [previousAdmin, newAdmin] = receipt.events.map( + (e) => e.event === 'AdminChanged' && e + )[0].args + + expect(previousAdmin).to.eq(await admin.getAddress()) + expect(newAdmin).to.eq(await voter1.getAddress()) + }) + + it('Should NOT be able to change the current proxy admin if the caller is not an admin', async () => { + let error + + const tx = await voteProxy + .connect(voter1) + .changeAdmin(await voter1.getAddress()) + + expect(tx.wait()).to.eventually.be.rejected.and.have.property( + 'code', + CALL_EXCEPTION + ) + }) + }) + + describe('Implementation contract', () => { + it('V1: Should load VoteV1 into proxy address', async () => { + proxiedVoteV1 = new ethers.Contract( + voteProxy.address, + VoteV1Artifact.abi, + admin + ) + await proxiedVoteV1.initialize() + + expect(await proxiedVoteV1.version()).to.eq(1) + expect(proxiedVoteV1.address).to.eq(voteProxy.address) + }) + + it('V1: Should cast votes to the system', async () => { + await proxiedVoteV1.connect(voter1).vote() + await proxiedVoteV1.connect(voter2).vote() + + const voters = await proxiedVoteV1.voters() + + expect(voters[0]).to.eq(voter1.address) + expect(voters[1]).to.eq(voter2.address) + }) + + it('V1: Should check if an account has already voted', async () => { + const votedStatus = await proxiedVoteV1.voted(voter1.address) + expect(votedStatus).to.be.true + }) + + it('V1: Should NOT let an already voted account to cast another vote', async () => { + const invalidVoteTx = await proxiedVoteV1 + .connect(voter1) + .vote(GAS_LIMIT_1_000_000) + + expect(invalidVoteTx.wait()).to.eventually.be.rejected.and.have.property( + 'code', + CALL_EXCEPTION + ) + }) + + it('V2: Should load VoteV2 into proxy address', async () => { + const tx = await voteProxy.upgradeToAndCall(voteV2.address, EMPTY_DATA) + await tx.wait() + + proxiedVoteV2 = new ethers.Contract( + voteProxy.address, + VoteV2Artifact.abi, + admin + ) + await proxiedVoteV2.initializeV2() + + expect(await proxiedVoteV2.version()).to.eq(2) + expect(proxiedVoteV2.address).to.eq(voteProxy.address) + }) + + it('V2: Should correctly inherit the storage states from version 1', async () => { + const voters = await proxiedVoteV2.voters() + + expect(voters[0]).to.eq(voter1.address) + expect(voters[1]).to.eq(voter2.address) + }) + + it('V2: Should let voters withdraw their votes which is only available in VoteV2', async () => { + await proxiedVoteV2.connect(voter1).withdrawVote() + expect(await proxiedVoteV2.voted(voter1.address)).to.be.false + }) + }) +})