diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d92583e..470a94c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: - - main + branches: [main, dev] pull_request: + branches: [main, dev, feat/*] jobs: build: @@ -42,11 +42,13 @@ jobs: - name: Run Forge build run: | + npm run generate:interfaces forge --version forge build --sizes id: build - name: Run Forge tests - run: | - forge test -vvv + env: + INFURA_TOKEN: ${{ secrets.INFURA_TOKEN }} + run: forge test -vvv id: test diff --git a/.gitignore b/.gitignore index 11887e4e..9ff034ca 100644 --- a/.gitignore +++ b/.gitignore @@ -6,11 +6,13 @@ contractAddresses.json # ignore generated files +broadcast/*/*/dry-run contracts/root/predicates/TransferWithSigUtils.sol contracts/common/mixin/ChainIdMixin.sol test/helpers/marketplaceUtils.js -cache_hardhat -forge-cache +scripts/helpers/interfaces +cache_hardhat/ +forge-cache/ test-bor-docker/history diff --git a/.gitmodules b/.gitmodules index 3b131006..638b2891 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,4 +9,7 @@ url = https://github.com/0xPolygon/contract-deployer-template [submodule "lib/storage-layout-checker"] path = lib/storage-layout-checker - url = https://github.com/0xPolygon/storage-layout-checker \ No newline at end of file + url = https://github.com/0xPolygon/storage-layout-checker +[submodule "lib/forge-std"] + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std diff --git a/README.md b/README.md index b2047add..182ed875 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,20 @@ Ethereum smart contracts that power the [Matic Network](https://polygon.technology/polygon-pos). +## Development ### Install dependencies with ``` npm install ``` -### Setup +### Setup git hooks ``` pre-commit install ``` -### Compile +### Prepare templates ``` npm run template:process -- --bor-chain-id 15001 @@ -27,7 +28,30 @@ bor-chain-id should be: Mainnet = 137 TestnetV4 (Mumbai) = 80001 -### Main chain and side chain +### Generate interfaces + +``` +npm run generate:interfaces +``` + +### Build + +``` +forge build +``` + +## Testing + +### Run forge upgrade forktest + +``` +forge test +``` + +### Run unit tests + + +#### Main chain and side chain - Main chain @@ -51,7 +75,7 @@ npm run bor:stop npm run bor:clean ``` -### Run tests +#### Run tests Run Hardhat test @@ -59,12 +83,6 @@ Run Hardhat test npm test:hardhat ``` -Run Foundry test - -``` -npm test:foundry -``` - ### Coverage Run coverage with diff --git a/broadcast/UpgradeDepositManager_Sepolia.s.sol/11155111/run-1721057237.json b/broadcast/UpgradeDepositManager_Sepolia.s.sol/11155111/run-1721057237.json new file mode 100644 index 00000000..b0c4eb8b --- /dev/null +++ b/broadcast/UpgradeDepositManager_Sepolia.s.sol/11155111/run-1721057237.json @@ -0,0 +1,62 @@ +{ + "transactions": [ + { + "hash": "0x7791e1d6b77d0b8483bc9294d1562089b2257f9e478014154dc9dc6e70ca7afc", + "transactionType": "CREATE", + "contractName": null, + "contractAddress": "0x332d42a3c83fbc46f27137278018a589c637ce3b", + "function": null, + "arguments": null, + "transaction": { + "from": "0x0724d72eb61e508d81ca701881f2248f092953bf", + "gas": "0x23dd76", + "value": "0x0", + "input": "0x608060405268056bc75e2d6310000060085534801561001d57600080fd5b50600080546001600160a01b031916331780825560405182916001600160a01b03169082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3600280546001600160a01b0319166001600160a01b039290921691909117905550611ed9806100976000396000f3fe6080604052600436106101815760003560e01c80638f32d59b116100d1578063cb10f94c1161008a578063e7af7ba111610064578063e7af7ba11461063c578063f220371114610663578063f2fde38b14610696578063f83d08ba146106c957610181565b8063cb10f94c146105d9578063cf309012146105ee578063d29a4bf61461060357610181565b80638f32d59b1461050a57806397feb92614610533578063987ab9db1461056c57806398ea5fca14610181578063a69df4b514610581578063b02c43d01461059657610181565b80635aa6e6751161013e5780637b103999116101185780637b103999146103c55780637b1f7117146103da5780638b9e4f93146104b25780638da5cb5b146104f557610181565b80635aa6e675146103715780636567082814610386578063715018a6146103b057610181565b8063072b15351461018b578063150b7a02146101ce57806342be8379146102be57806342fc47fb146102d357806349f4cc17146103045780634b56c07114610347575b6101896106de565b005b34801561019757600080fd5b50610189600480360360608110156101ae57600080fd5b506001600160a01b038135811691602081013590911690604001356107c0565b3480156101da57600080fd5b506102a1600480360360808110156101f157600080fd5b6001600160a01b0382358116926020810135909116916040820135919081019060808101606082013564010000000081111561022c57600080fd5b82018360208201111561023e57600080fd5b8035906020019184600183028401116401000000008311171561026057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610892945050505050565b604080516001600160e01b03199092168252519081900360200190f35b3480156102ca57600080fd5b506101896108a2565b3480156102df57600080fd5b506102e86109b3565b604080516001600160a01b039092168252519081900360200190f35b34801561031057600080fd5b506101896004803603606081101561032757600080fd5b506001600160a01b038135811691602081013590911690604001356109c2565b34801561035357600080fd5b506101896004803603602081101561036a57600080fd5b5035610f42565b34801561037d57600080fd5b506102e8610f88565b34801561039257600080fd5b50610189600480360360208110156103a957600080fd5b5035610f97565b3480156103bc57600080fd5b50610189610fab565b3480156103d157600080fd5b506102e8611006565b3480156103e657600080fd5b50610189600480360360608110156103fd57600080fd5b81019060208101813564010000000081111561041857600080fd5b82018360208201111561042a57600080fd5b8035906020019184602083028401116401000000008311171561044c57600080fd5b91939092909160208101903564010000000081111561046a57600080fd5b82018360208201111561047c57600080fd5b8035906020019184602083028401116401000000008311171561049e57600080fd5b9193509150356001600160a01b0316611015565b3480156104be57600080fd5b50610189600480360360608110156104d557600080fd5b506001600160a01b0381358116916020810135909116906040013561127f565b34801561050157600080fd5b506102e86112f1565b34801561051657600080fd5b5061051f611300565b604080519115158252519081900360200190f35b34801561053f57600080fd5b506101896004803603604081101561055657600080fd5b506001600160a01b038135169060200135611311565b34801561057857600080fd5b506102e861131c565b34801561058d57600080fd5b5061018961132b565b3480156105a257600080fd5b506105c0600480360360208110156105b957600080fd5b503561133d565b6040805192835260208301919091528051918290030190f35b3480156105e557600080fd5b506102e8611356565b3480156105fa57600080fd5b5061051f611365565b34801561060f57600080fd5b506101896004803603604081101561062657600080fd5b506001600160a01b038135169060200135611375565b34801561064857600080fd5b50610651611380565b60408051918252519081900360200190f35b34801561066f57600080fd5b506101896004803603602081101561068657600080fd5b50356001600160a01b0316611386565b3480156106a257600080fd5b50610189600480360360208110156106b957600080fd5b50356001600160a01b03166113b9565b3480156106d557600080fd5b506101896113d3565b60035460408051638b9c948960e01b815290516000926001600160a01b031691638b9c9489916004808301926020929190829003018186803b15801561072357600080fd5b505afa158015610737573d6000803e3d6000fd5b505050506040513d602081101561074d57600080fd5b505160408051630d0e30db60e41b8152905191925082916001600160a01b0383169163d0e30db091349160048082019260009290919082900301818588803b15801561079857600080fd5b505af11580156107ac573d6000803e3d6000fd5b50505050506107bc3383346113e3565b5050565b600354604080516301f07db560e01b81526001600160a01b038681166004830152915191909216916301f07db5916024808301926020929190829003018186803b15801561080d57600080fd5b505afa158015610821573d6000803e3d6000fd5b505050506040513d602081101561083757600080fd5b5051610877576040805162461bcd60e51b815260206004820152600a6024820152696e6f742065726337323160b01b604482015290519081900360640190fd5b610882338483611541565b61088d8284836113e3565b505050565b630a85bd0160e11b949350505050565b60035460408051630c91702f60e31b8152815160009384936001600160a01b039091169263648b81789260048083019392829003018186803b1580156108e757600080fd5b505afa1580156108fb573d6000803e3d6000fd5b505050506040513d604081101561091157600080fd5b50805160209091015160055491935091506001600160a01b03808316911614158061094a57506007546001600160a01b03838116911614155b6109855760405162461bcd60e51b815260040180806020018281038252603e815260200180611e41603e913960400191505060405180910390fd5b600780546001600160a01b039384166001600160a01b03199182161790915560058054929093169116179055565b6007546001600160a01b031681565b600354604080516337b1d58560e01b815233600482015290516001600160a01b03909216916337b1d58591602480820192602092909190829003018186803b158015610a0d57600080fd5b505afa158015610a21573d6000803e3d6000fd5b505050506040513d6020811015610a3757600080fd5b50516003811115610a4457fe5b60ff16610a90576040805162461bcd60e51b81526020600482015260156024820152744e6f7420612076616c69642070726564696361746560581b604482015290519081900360640190fd5b60035460408051638b9c948960e01b815290516000926001600160a01b031691638b9c9489916004808301926020929190829003018186803b158015610ad557600080fd5b505afa158015610ae9573d6000803e3d6000fd5b505050506040513d6020811015610aff57600080fd5b5051600354604080516336a8279560e21b81526001600160a01b038881166004830152915193945091169163daa09e5491602480820192602092909190829003018186803b158015610b5057600080fd5b505afa158015610b64573d6000803e3d6000fd5b505050506040513d6020811015610b7a57600080fd5b505115610bf657604080516323b872dd60e01b81523060048201526001600160a01b038581166024830152604482018590529151918616916323b872dd9160648082019260009290919082900301818387803b158015610bd957600080fd5b505af1158015610bed573d6000803e3d6000fd5b50505050610f3c565b806001600160a01b0316846001600160a01b03161415610c7e5760408051627b8a6760e11b8152600481018490526001600160a01b0385811660248301529151869283169162f714ce91604480830192600092919082900301818387803b158015610c6057600080fd5b505af1158015610c74573d6000803e3d6000fd5b5050505050610f3c565b60035460408051646d6174696360d81b8152815190819003600501812063cac39a0560e01b8252600482015290516001600160a01b039092169163cac39a0591602480820192602092909190829003018186803b158015610cde57600080fd5b505afa158015610cf2573d6000803e3d6000fd5b505050506040513d6020811015610d0857600080fd5b50516001600160a01b0385811691161415610e6d576003805460408051621c1bdb60ea1b8152815190819003909301832063cac39a0560e01b84526004840152516001600160a01b039091169163cac39a05916024808301926020929190829003018186803b158015610d7a57600080fd5b505afa158015610d8e573d6000803e3d6000fd5b505050506040513d6020811015610da457600080fd5b50516040805163a9059cbb60e01b81526001600160a01b038681166004830152602482018690529151919092169163a9059cbb9160448083019260209291908290030181600087803b158015610df957600080fd5b505af1158015610e0d573d6000803e3d6000fd5b505050506040513d6020811015610e2357600080fd5b5051610e68576040805162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b604482015290519081900360640190fd5b610f3c565b836001600160a01b031663a9059cbb84846040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015610ecd57600080fd5b505af1158015610ee1573d6000803e3d6000fd5b505050506040513d6020811015610ef757600080fd5b5051610f3c576040805162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b604482015290519081900360640190fd5b50505050565b610f4a6115b6565b80610f5457600080fd5b6008546040518291907f010c0265813c273963aa5e8683cf5c45a3b744ba6369c22af0958ec5fcf16b2090600090a3600855565b6002546001600160a01b031681565b610f9f6115b6565b610fa8816115ff565b50565b610fb3611300565b610fbc57600080fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6003546001600160a01b031681565b61101d6118b0565b838214611061576040805162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a5908125b9c1d5d609a1b604482015290519081900360640190fd5b6004805460408051635391f48360e01b8152928301879052516000926001600160a01b0390921691635391f48391602480830192602092919082900301818787803b1580156110af57600080fd5b505af11580156110c3573d6000803e3d6000fd5b505050506040513d60208110156110d957600080fd5b50516003549091506001600160a01b031660005b8681101561127557816001600160a01b03166301f07db589898481811061111057fe5b905060200201356001600160a01b03166040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561116657600080fd5b505afa15801561117a573d6000803e3d6000fd5b505050506040513d602081101561119057600080fd5b5051156111d4576111cf338989848181106111a757fe5b905060200201356001600160a01b03168888858181106111c357fe5b90506020020135611541565b611221565b61122133308888858181106111e557fe5b905060200201358b8b868181106111f857fe5b905060200201356001600160a01b03166001600160a01b03166118f8909392919063ffffffff16565b61125a8489898481811061123157fe5b905060200201356001600160a01b031688888581811061124d57fe5b9050602002013586611952565b61126b83600163ffffffff611c6d16565b92506001016110ed565b5050505050505050565b6008548111156112d6576040805162461bcd60e51b815260206004820152601d60248201527f657863656564206d6178696d756d206465706f73697420616d6f756e74000000604482015290519081900360640190fd5b6108826001600160a01b03841633308463ffffffff6118f816565b6000546001600160a01b031690565b6000546001600160a01b0316331490565b6107bc82338361127f565b6004546001600160a01b031681565b6113336115b6565b61133b611c86565b565b6006602052600090815260409020805460019091015482565b6005546001600160a01b031681565b600154600160a01b900460ff1681565b6107bc8233836107c0565b60085481565b61138e611300565b61139757600080fd5b600480546001600160a01b0319166001600160a01b0392909216919091179055565b6113c1611300565b6113ca57600080fd5b610fa881611c95565b6113db6115b6565b61133b611d03565b6113eb6118b0565b60035460408051636416c18360e01b81526001600160a01b038086166004830152915185939290921691636416c18391602480820192602092909190829003018186803b15801561143b57600080fd5b505afa15801561144f573d6000803e3d6000fd5b505050506040513d602081101561146557600080fd5b50516114ae576040805162461bcd60e51b81526020600482015260136024820152721513d2d15397d393d517d4d5541413d4951151606a1b604482015290519081900360640190fd5b610f3c848484600460009054906101000a90046001600160a01b03166001600160a01b0316635391f48360016040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561151057600080fd5b505af1158015611524573d6000803e3d6000fd5b505050506040513d602081101561153a57600080fd5b5051611952565b60408051632142170760e11b81526001600160a01b038581166004830152306024830152604482018490529151918416916342842e0e9160648082019260009290919082900301818387803b15801561159957600080fd5b505af11580156115ad573d6000803e3d6000fd5b50505050505050565b6002546001600160a01b0316331461133b5760405162461bcd60e51b8152600401808060200182810382526026815260200180611e7f6026913960400191505060405180910390fd5b60035460408051646d6174696360d81b8152815190819003600501812063cac39a0560e01b8252600482015290516000926001600160a01b03169163cac39a05916024808301926020929190829003018186803b15801561165f57600080fd5b505afa158015611673573d6000803e3d6000fd5b505050506040513d602081101561168957600080fd5b5051600354604080516f3837b63cb3b7b726b4b3b930ba34b7b760811b8152815190819003601001812063cac39a0560e01b8252600482015290519293506000926001600160a01b039092169163cac39a0591602480820192602092909190829003018186803b1580156116fc57600080fd5b505afa158015611710573d6000803e3d6000fd5b505050506040513d602081101561172657600080fd5b5051604080516370a0823160e01b8152306004820152905191925084916001600160a01b038516916370a08231916024808301926020929190829003018186803b15801561177357600080fd5b505afa158015611787573d6000803e3d6000fd5b505050506040513d602081101561179d57600080fd5b505110156117dc5760405162461bcd60e51b815260040180806020018281038252602c815260200180611e15602c913960400191505060405180910390fd5b816001600160a01b031663095ea7b382856040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561183c57600080fd5b505af1158015611850573d6000803e3d6000fd5b505050506040513d602081101561186657600080fd5b5050604080516308a960c160e31b81526004810185905290516001600160a01b0383169163454b060891602480830192600092919082900301818387803b15801561159957600080fd5b600154600160a01b900460ff161561133b576040805162461bcd60e51b81526020600482015260066024820152651b1bd8dad95960d21b604482015290519081900360640190fd5b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052610f3c908590611d18565b60035460408051646d6174696360d81b8152815190819003600501812063cac39a0560e01b8252600482015290516000926001600160a01b03169163cac39a05916024808301926020929190829003018186803b1580156119b257600080fd5b505afa1580156119c6573d6000803e3d6000fd5b505050506040513d60208110156119dc57600080fd5b505190506001600160a01b038481169082161415611a02576119fd836115ff565b611aa2565b6003805460408051621c1bdb60ea1b8152815190819003909301832063cac39a0560e01b84526004840152516001600160a01b039091169163cac39a05916024808301926020929190829003018186803b158015611a5f57600080fd5b505afa158015611a73573d6000803e3d6000fd5b505050506040513d6020811015611a8957600080fd5b50516001600160a01b0385811691161415611aa2578093505b604080518082018252606087811b6bffffffffffffffffffffffff199081168284015287821b16607483015260888083018790528351808403909101815260a883018452805160209182012083524281840190815260008781526006835285812094518555905160019094019390935560055460075485516001600160a01b03808d1682860152808c16828901529481018a905260808082018a90528751808303909101815260a082018089526316f1983160e01b905291851660a4820181815260c48301988952835160e4840152835196909416976316f198319791969395909361010490930192908601918190849084905b83811015611bae578181015183820152602001611b96565b50505050905090810190601f168015611bdb5780820380516001836020036101000a031916815260200191505b509350505050600060405180830381600087803b158015611bfb57600080fd5b505af1158015611c0f573d6000803e3d6000fd5b50505050836001600160a01b0316856001600160a01b03167f1dadc8d0683c6f9824e885935c1bec6f76816730dcec148dda8cf25a7b9f797b8585604051808381526020018281526020019250505060405180910390a35050505050565b600082820183811015611c7f57600080fd5b9392505050565b6001805460ff60a01b19169055565b6001600160a01b038116611ca857600080fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6001805460ff60a01b1916600160a01b179055565b611d2a826001600160a01b0316611e0e565b611d3357600080fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310611d715780518252601f199092019160209182019101611d52565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114611dd3576040519150601f19603f3d011682016040523d82523d6000602084013e611dd8565b606091505b509150915081611de757600080fd5b805115610f3c57808060200190516020811015611e0357600080fd5b5051610f3c57600080fd5b3b15159056fe616d6f756e742065786365656473207468697320636f6e74726163742773204d415449432062616c616e636541746c65617374206f6e65206f6620737461746553656e646572206f72206368696c64436861696e20616464726573732073686f756c64206368616e67654f6e6c7920676f7665726e616e636520636f6e747261637420697320617574686f72697a6564a265627a7a72315820af90bf4264f58eb743bcfe1f448541fe7693fe6821f29e1b76d985e33a522baa64736f6c63430005110032", + "nonce": "0x8b", + "chainId": "0xaa36a7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x10b4441", + "logs": [ + { + "address": "0x332d42a3c83fbc46f27137278018a589c637ce3b", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000724d72eb61e508d81ca701881f2248f092953bf" + ], + "data": "0x", + "blockHash": "0x28a89052310fadb5f07811332a8c04d1752870b21e8ef659a712ce0a7c7ba782", + "blockNumber": "0x605ef4", + "transactionHash": "0x7791e1d6b77d0b8483bc9294d1562089b2257f9e478014154dc9dc6e70ca7afc", + "transactionIndex": "0x63", + "logIndex": "0xde", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000004000000800000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000400400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000010000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x7791e1d6b77d0b8483bc9294d1562089b2257f9e478014154dc9dc6e70ca7afc", + "transactionIndex": "0x63", + "blockHash": "0x28a89052310fadb5f07811332a8c04d1752870b21e8ef659a712ce0a7c7ba782", + "blockNumber": "0x605ef4", + "gasUsed": "0x1b98a2", + "effectiveGasPrice": "0x2e2fa16bc", + "from": "0x0724d72eb61e508d81ca701881f2248f092953bf", + "to": null, + "contractAddress": "0x332d42a3c83fbc46f27137278018a589c637ce3b" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1721057237, + "chain": 11155111, + "commit": "a5fd7d69" +} \ No newline at end of file diff --git a/broadcast/UpgradeDepositManager_Sepolia.s.sol/11155111/run-latest.json b/broadcast/UpgradeDepositManager_Sepolia.s.sol/11155111/run-latest.json new file mode 100644 index 00000000..b0c4eb8b --- /dev/null +++ b/broadcast/UpgradeDepositManager_Sepolia.s.sol/11155111/run-latest.json @@ -0,0 +1,62 @@ +{ + "transactions": [ + { + "hash": "0x7791e1d6b77d0b8483bc9294d1562089b2257f9e478014154dc9dc6e70ca7afc", + "transactionType": "CREATE", + "contractName": null, + "contractAddress": "0x332d42a3c83fbc46f27137278018a589c637ce3b", + "function": null, + "arguments": null, + "transaction": { + "from": "0x0724d72eb61e508d81ca701881f2248f092953bf", + "gas": "0x23dd76", + "value": "0x0", + "input": "0x608060405268056bc75e2d6310000060085534801561001d57600080fd5b50600080546001600160a01b031916331780825560405182916001600160a01b03169082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3600280546001600160a01b0319166001600160a01b039290921691909117905550611ed9806100976000396000f3fe6080604052600436106101815760003560e01c80638f32d59b116100d1578063cb10f94c1161008a578063e7af7ba111610064578063e7af7ba11461063c578063f220371114610663578063f2fde38b14610696578063f83d08ba146106c957610181565b8063cb10f94c146105d9578063cf309012146105ee578063d29a4bf61461060357610181565b80638f32d59b1461050a57806397feb92614610533578063987ab9db1461056c57806398ea5fca14610181578063a69df4b514610581578063b02c43d01461059657610181565b80635aa6e6751161013e5780637b103999116101185780637b103999146103c55780637b1f7117146103da5780638b9e4f93146104b25780638da5cb5b146104f557610181565b80635aa6e675146103715780636567082814610386578063715018a6146103b057610181565b8063072b15351461018b578063150b7a02146101ce57806342be8379146102be57806342fc47fb146102d357806349f4cc17146103045780634b56c07114610347575b6101896106de565b005b34801561019757600080fd5b50610189600480360360608110156101ae57600080fd5b506001600160a01b038135811691602081013590911690604001356107c0565b3480156101da57600080fd5b506102a1600480360360808110156101f157600080fd5b6001600160a01b0382358116926020810135909116916040820135919081019060808101606082013564010000000081111561022c57600080fd5b82018360208201111561023e57600080fd5b8035906020019184600183028401116401000000008311171561026057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610892945050505050565b604080516001600160e01b03199092168252519081900360200190f35b3480156102ca57600080fd5b506101896108a2565b3480156102df57600080fd5b506102e86109b3565b604080516001600160a01b039092168252519081900360200190f35b34801561031057600080fd5b506101896004803603606081101561032757600080fd5b506001600160a01b038135811691602081013590911690604001356109c2565b34801561035357600080fd5b506101896004803603602081101561036a57600080fd5b5035610f42565b34801561037d57600080fd5b506102e8610f88565b34801561039257600080fd5b50610189600480360360208110156103a957600080fd5b5035610f97565b3480156103bc57600080fd5b50610189610fab565b3480156103d157600080fd5b506102e8611006565b3480156103e657600080fd5b50610189600480360360608110156103fd57600080fd5b81019060208101813564010000000081111561041857600080fd5b82018360208201111561042a57600080fd5b8035906020019184602083028401116401000000008311171561044c57600080fd5b91939092909160208101903564010000000081111561046a57600080fd5b82018360208201111561047c57600080fd5b8035906020019184602083028401116401000000008311171561049e57600080fd5b9193509150356001600160a01b0316611015565b3480156104be57600080fd5b50610189600480360360608110156104d557600080fd5b506001600160a01b0381358116916020810135909116906040013561127f565b34801561050157600080fd5b506102e86112f1565b34801561051657600080fd5b5061051f611300565b604080519115158252519081900360200190f35b34801561053f57600080fd5b506101896004803603604081101561055657600080fd5b506001600160a01b038135169060200135611311565b34801561057857600080fd5b506102e861131c565b34801561058d57600080fd5b5061018961132b565b3480156105a257600080fd5b506105c0600480360360208110156105b957600080fd5b503561133d565b6040805192835260208301919091528051918290030190f35b3480156105e557600080fd5b506102e8611356565b3480156105fa57600080fd5b5061051f611365565b34801561060f57600080fd5b506101896004803603604081101561062657600080fd5b506001600160a01b038135169060200135611375565b34801561064857600080fd5b50610651611380565b60408051918252519081900360200190f35b34801561066f57600080fd5b506101896004803603602081101561068657600080fd5b50356001600160a01b0316611386565b3480156106a257600080fd5b50610189600480360360208110156106b957600080fd5b50356001600160a01b03166113b9565b3480156106d557600080fd5b506101896113d3565b60035460408051638b9c948960e01b815290516000926001600160a01b031691638b9c9489916004808301926020929190829003018186803b15801561072357600080fd5b505afa158015610737573d6000803e3d6000fd5b505050506040513d602081101561074d57600080fd5b505160408051630d0e30db60e41b8152905191925082916001600160a01b0383169163d0e30db091349160048082019260009290919082900301818588803b15801561079857600080fd5b505af11580156107ac573d6000803e3d6000fd5b50505050506107bc3383346113e3565b5050565b600354604080516301f07db560e01b81526001600160a01b038681166004830152915191909216916301f07db5916024808301926020929190829003018186803b15801561080d57600080fd5b505afa158015610821573d6000803e3d6000fd5b505050506040513d602081101561083757600080fd5b5051610877576040805162461bcd60e51b815260206004820152600a6024820152696e6f742065726337323160b01b604482015290519081900360640190fd5b610882338483611541565b61088d8284836113e3565b505050565b630a85bd0160e11b949350505050565b60035460408051630c91702f60e31b8152815160009384936001600160a01b039091169263648b81789260048083019392829003018186803b1580156108e757600080fd5b505afa1580156108fb573d6000803e3d6000fd5b505050506040513d604081101561091157600080fd5b50805160209091015160055491935091506001600160a01b03808316911614158061094a57506007546001600160a01b03838116911614155b6109855760405162461bcd60e51b815260040180806020018281038252603e815260200180611e41603e913960400191505060405180910390fd5b600780546001600160a01b039384166001600160a01b03199182161790915560058054929093169116179055565b6007546001600160a01b031681565b600354604080516337b1d58560e01b815233600482015290516001600160a01b03909216916337b1d58591602480820192602092909190829003018186803b158015610a0d57600080fd5b505afa158015610a21573d6000803e3d6000fd5b505050506040513d6020811015610a3757600080fd5b50516003811115610a4457fe5b60ff16610a90576040805162461bcd60e51b81526020600482015260156024820152744e6f7420612076616c69642070726564696361746560581b604482015290519081900360640190fd5b60035460408051638b9c948960e01b815290516000926001600160a01b031691638b9c9489916004808301926020929190829003018186803b158015610ad557600080fd5b505afa158015610ae9573d6000803e3d6000fd5b505050506040513d6020811015610aff57600080fd5b5051600354604080516336a8279560e21b81526001600160a01b038881166004830152915193945091169163daa09e5491602480820192602092909190829003018186803b158015610b5057600080fd5b505afa158015610b64573d6000803e3d6000fd5b505050506040513d6020811015610b7a57600080fd5b505115610bf657604080516323b872dd60e01b81523060048201526001600160a01b038581166024830152604482018590529151918616916323b872dd9160648082019260009290919082900301818387803b158015610bd957600080fd5b505af1158015610bed573d6000803e3d6000fd5b50505050610f3c565b806001600160a01b0316846001600160a01b03161415610c7e5760408051627b8a6760e11b8152600481018490526001600160a01b0385811660248301529151869283169162f714ce91604480830192600092919082900301818387803b158015610c6057600080fd5b505af1158015610c74573d6000803e3d6000fd5b5050505050610f3c565b60035460408051646d6174696360d81b8152815190819003600501812063cac39a0560e01b8252600482015290516001600160a01b039092169163cac39a0591602480820192602092909190829003018186803b158015610cde57600080fd5b505afa158015610cf2573d6000803e3d6000fd5b505050506040513d6020811015610d0857600080fd5b50516001600160a01b0385811691161415610e6d576003805460408051621c1bdb60ea1b8152815190819003909301832063cac39a0560e01b84526004840152516001600160a01b039091169163cac39a05916024808301926020929190829003018186803b158015610d7a57600080fd5b505afa158015610d8e573d6000803e3d6000fd5b505050506040513d6020811015610da457600080fd5b50516040805163a9059cbb60e01b81526001600160a01b038681166004830152602482018690529151919092169163a9059cbb9160448083019260209291908290030181600087803b158015610df957600080fd5b505af1158015610e0d573d6000803e3d6000fd5b505050506040513d6020811015610e2357600080fd5b5051610e68576040805162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b604482015290519081900360640190fd5b610f3c565b836001600160a01b031663a9059cbb84846040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015610ecd57600080fd5b505af1158015610ee1573d6000803e3d6000fd5b505050506040513d6020811015610ef757600080fd5b5051610f3c576040805162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b604482015290519081900360640190fd5b50505050565b610f4a6115b6565b80610f5457600080fd5b6008546040518291907f010c0265813c273963aa5e8683cf5c45a3b744ba6369c22af0958ec5fcf16b2090600090a3600855565b6002546001600160a01b031681565b610f9f6115b6565b610fa8816115ff565b50565b610fb3611300565b610fbc57600080fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6003546001600160a01b031681565b61101d6118b0565b838214611061576040805162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a5908125b9c1d5d609a1b604482015290519081900360640190fd5b6004805460408051635391f48360e01b8152928301879052516000926001600160a01b0390921691635391f48391602480830192602092919082900301818787803b1580156110af57600080fd5b505af11580156110c3573d6000803e3d6000fd5b505050506040513d60208110156110d957600080fd5b50516003549091506001600160a01b031660005b8681101561127557816001600160a01b03166301f07db589898481811061111057fe5b905060200201356001600160a01b03166040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561116657600080fd5b505afa15801561117a573d6000803e3d6000fd5b505050506040513d602081101561119057600080fd5b5051156111d4576111cf338989848181106111a757fe5b905060200201356001600160a01b03168888858181106111c357fe5b90506020020135611541565b611221565b61122133308888858181106111e557fe5b905060200201358b8b868181106111f857fe5b905060200201356001600160a01b03166001600160a01b03166118f8909392919063ffffffff16565b61125a8489898481811061123157fe5b905060200201356001600160a01b031688888581811061124d57fe5b9050602002013586611952565b61126b83600163ffffffff611c6d16565b92506001016110ed565b5050505050505050565b6008548111156112d6576040805162461bcd60e51b815260206004820152601d60248201527f657863656564206d6178696d756d206465706f73697420616d6f756e74000000604482015290519081900360640190fd5b6108826001600160a01b03841633308463ffffffff6118f816565b6000546001600160a01b031690565b6000546001600160a01b0316331490565b6107bc82338361127f565b6004546001600160a01b031681565b6113336115b6565b61133b611c86565b565b6006602052600090815260409020805460019091015482565b6005546001600160a01b031681565b600154600160a01b900460ff1681565b6107bc8233836107c0565b60085481565b61138e611300565b61139757600080fd5b600480546001600160a01b0319166001600160a01b0392909216919091179055565b6113c1611300565b6113ca57600080fd5b610fa881611c95565b6113db6115b6565b61133b611d03565b6113eb6118b0565b60035460408051636416c18360e01b81526001600160a01b038086166004830152915185939290921691636416c18391602480820192602092909190829003018186803b15801561143b57600080fd5b505afa15801561144f573d6000803e3d6000fd5b505050506040513d602081101561146557600080fd5b50516114ae576040805162461bcd60e51b81526020600482015260136024820152721513d2d15397d393d517d4d5541413d4951151606a1b604482015290519081900360640190fd5b610f3c848484600460009054906101000a90046001600160a01b03166001600160a01b0316635391f48360016040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561151057600080fd5b505af1158015611524573d6000803e3d6000fd5b505050506040513d602081101561153a57600080fd5b5051611952565b60408051632142170760e11b81526001600160a01b038581166004830152306024830152604482018490529151918416916342842e0e9160648082019260009290919082900301818387803b15801561159957600080fd5b505af11580156115ad573d6000803e3d6000fd5b50505050505050565b6002546001600160a01b0316331461133b5760405162461bcd60e51b8152600401808060200182810382526026815260200180611e7f6026913960400191505060405180910390fd5b60035460408051646d6174696360d81b8152815190819003600501812063cac39a0560e01b8252600482015290516000926001600160a01b03169163cac39a05916024808301926020929190829003018186803b15801561165f57600080fd5b505afa158015611673573d6000803e3d6000fd5b505050506040513d602081101561168957600080fd5b5051600354604080516f3837b63cb3b7b726b4b3b930ba34b7b760811b8152815190819003601001812063cac39a0560e01b8252600482015290519293506000926001600160a01b039092169163cac39a0591602480820192602092909190829003018186803b1580156116fc57600080fd5b505afa158015611710573d6000803e3d6000fd5b505050506040513d602081101561172657600080fd5b5051604080516370a0823160e01b8152306004820152905191925084916001600160a01b038516916370a08231916024808301926020929190829003018186803b15801561177357600080fd5b505afa158015611787573d6000803e3d6000fd5b505050506040513d602081101561179d57600080fd5b505110156117dc5760405162461bcd60e51b815260040180806020018281038252602c815260200180611e15602c913960400191505060405180910390fd5b816001600160a01b031663095ea7b382856040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561183c57600080fd5b505af1158015611850573d6000803e3d6000fd5b505050506040513d602081101561186657600080fd5b5050604080516308a960c160e31b81526004810185905290516001600160a01b0383169163454b060891602480830192600092919082900301818387803b15801561159957600080fd5b600154600160a01b900460ff161561133b576040805162461bcd60e51b81526020600482015260066024820152651b1bd8dad95960d21b604482015290519081900360640190fd5b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052610f3c908590611d18565b60035460408051646d6174696360d81b8152815190819003600501812063cac39a0560e01b8252600482015290516000926001600160a01b03169163cac39a05916024808301926020929190829003018186803b1580156119b257600080fd5b505afa1580156119c6573d6000803e3d6000fd5b505050506040513d60208110156119dc57600080fd5b505190506001600160a01b038481169082161415611a02576119fd836115ff565b611aa2565b6003805460408051621c1bdb60ea1b8152815190819003909301832063cac39a0560e01b84526004840152516001600160a01b039091169163cac39a05916024808301926020929190829003018186803b158015611a5f57600080fd5b505afa158015611a73573d6000803e3d6000fd5b505050506040513d6020811015611a8957600080fd5b50516001600160a01b0385811691161415611aa2578093505b604080518082018252606087811b6bffffffffffffffffffffffff199081168284015287821b16607483015260888083018790528351808403909101815260a883018452805160209182012083524281840190815260008781526006835285812094518555905160019094019390935560055460075485516001600160a01b03808d1682860152808c16828901529481018a905260808082018a90528751808303909101815260a082018089526316f1983160e01b905291851660a4820181815260c48301988952835160e4840152835196909416976316f198319791969395909361010490930192908601918190849084905b83811015611bae578181015183820152602001611b96565b50505050905090810190601f168015611bdb5780820380516001836020036101000a031916815260200191505b509350505050600060405180830381600087803b158015611bfb57600080fd5b505af1158015611c0f573d6000803e3d6000fd5b50505050836001600160a01b0316856001600160a01b03167f1dadc8d0683c6f9824e885935c1bec6f76816730dcec148dda8cf25a7b9f797b8585604051808381526020018281526020019250505060405180910390a35050505050565b600082820183811015611c7f57600080fd5b9392505050565b6001805460ff60a01b19169055565b6001600160a01b038116611ca857600080fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6001805460ff60a01b1916600160a01b179055565b611d2a826001600160a01b0316611e0e565b611d3357600080fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310611d715780518252601f199092019160209182019101611d52565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114611dd3576040519150601f19603f3d011682016040523d82523d6000602084013e611dd8565b606091505b509150915081611de757600080fd5b805115610f3c57808060200190516020811015611e0357600080fd5b5051610f3c57600080fd5b3b15159056fe616d6f756e742065786365656473207468697320636f6e74726163742773204d415449432062616c616e636541746c65617374206f6e65206f6620737461746553656e646572206f72206368696c64436861696e20616464726573732073686f756c64206368616e67654f6e6c7920676f7665726e616e636520636f6e747261637420697320617574686f72697a6564a265627a7a72315820af90bf4264f58eb743bcfe1f448541fe7693fe6821f29e1b76d985e33a522baa64736f6c63430005110032", + "nonce": "0x8b", + "chainId": "0xaa36a7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x10b4441", + "logs": [ + { + "address": "0x332d42a3c83fbc46f27137278018a589c637ce3b", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000724d72eb61e508d81ca701881f2248f092953bf" + ], + "data": "0x", + "blockHash": "0x28a89052310fadb5f07811332a8c04d1752870b21e8ef659a712ce0a7c7ba782", + "blockNumber": "0x605ef4", + "transactionHash": "0x7791e1d6b77d0b8483bc9294d1562089b2257f9e478014154dc9dc6e70ca7afc", + "transactionIndex": "0x63", + "logIndex": "0xde", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000004000000800000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000400400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000010000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x7791e1d6b77d0b8483bc9294d1562089b2257f9e478014154dc9dc6e70ca7afc", + "transactionIndex": "0x63", + "blockHash": "0x28a89052310fadb5f07811332a8c04d1752870b21e8ef659a712ce0a7c7ba782", + "blockNumber": "0x605ef4", + "gasUsed": "0x1b98a2", + "effectiveGasPrice": "0x2e2fa16bc", + "from": "0x0724d72eb61e508d81ca701881f2248f092953bf", + "to": null, + "contractAddress": "0x332d42a3c83fbc46f27137278018a589c637ce3b" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1721057237, + "chain": 11155111, + "commit": "a5fd7d69" +} \ No newline at end of file diff --git a/broadcast/UpgradeStakeManager_Sepolia.s.sol/11155111/run-1721057742.json b/broadcast/UpgradeStakeManager_Sepolia.s.sol/11155111/run-1721057742.json new file mode 100644 index 00000000..eceb3423 --- /dev/null +++ b/broadcast/UpgradeStakeManager_Sepolia.s.sol/11155111/run-1721057742.json @@ -0,0 +1,112 @@ +{ + "transactions": [ + { + "hash": "0x4499d0ecf70c3a39927d5245cac653c02f0f0c33dc82be6a9ce5d37d4615c70a", + "transactionType": "CREATE", + "contractName": null, + "contractAddress": "0x777badc2c75bd4ee5303bf6e90009bd31d8fd3d1", + "function": null, + "arguments": null, + "transaction": { + "from": "0x0724d72eb61e508d81ca701881f2248f092953bf", + "gas": "0x6b19b6", + "value": "0x0", + "input": "0x60806040526023805460ff191690553480156200001b57600080fd5b5060008054610100600160a81b0319168155600180546001600160a01b0319163317908190556040516001600160a01b039190911691907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a360235460ff1615620000c1576040805162461bcd60e51b815260206004820152600e60248201526d185b1c9958591e481a5b9a5d195960921b604482015290519081900360640190fd5b6023805460ff19166001179055615fc580620000de6000396000f3fe608060405234801561001057600080fd5b50600436106107175760003560e01c80637d669752116103a8578063b1d23f02116101f4578063e8afa8e81161011a578063f5e95acb116100b8578063fb1ef52c11610087578063fb1ef52c14611826578063fba58f341461184f578063fc0c546a14611857578063ff3db8e31461185f57610717565b8063f5e95acb1461178f578063f771fc87146117f9578063f83d08ba14611816578063f8a3176c1461181e57610717565b8063f24ccbfe116100f4578063f24ccbfe14611697578063f28699fa1461169f578063f2fde38b146116be578063f41a9642146116e457610717565b8063e8afa8e81461164c578063eceec1d314611672578063f1674fdf1461168f57610717565b8063cf30901211610192578063dcd962b211610161578063dcd962b2146115fc578063e568959a1461161f578063e59ee0c614611627578063e6692f491461162f57610717565b8063cf3090121461159b578063d6de07d0146115a3578063d7f5549d146115c2578063d86d53e7146115df57610717565b8063c710e922116101ce578063c710e92214611518578063c7f067cb14611544578063cbf383d514611576578063cd6b83881461159357610717565b8063b1d23f02146114a6578063b65de35e146114c9578063bc8756a9146114e657610717565b80639342c8f4116102d95780639ff1150011610277578063a69df4b511610246578063a69df4b514611462578063a7ab69611461146a578063a8dc889b14611472578063b184be811461147a57610717565b80639ff1150014611397578063a440ab1e146113ba578063a4769071146113d7578063a6854877146113df57610717565b806399d18f6f116102b357806399d18f6f1461132c5780639a8a62431461134f5780639b33f434146113575780639ddbbf851461137457610717565b80639342c8f4146112dc57806396a93224146112f9578063987ab9db1461132457610717565b806386a22666116103465780638f32d59b116103205780638f32d59b146112a7578063900cf0cf146112af57806391460149146112b757806391f1a3a5146112d457610717565b806386a226661461127a578063883b455f146112975780638da5cb5b1461129f57610717565b80637f952d95116103825780637f952d951461118057806381254e1214611188578063817b1cd21461124f578063858a7c031461125757610717565b80637d6697521461113e5780637ed4b27c146111465780637f4b43231461116357610717565b806341b3d185116105675780635ab1bd53116104985780637060054d116104365780637854f354116104055780637854f354146110eb57806378f84a44146111085780637b1039991461112e5780637c7eaf1a1461113657610717565b80637060054d146110cb578063715018a6146110d357806376671808146110db57806377939d10146110e357610717565b80636352211e116104725780636352211e14610fa05780636365679814610fbd57806368cb812a14610fe95780636901b2531461109957610717565b80635ab1bd5314610f015780635e47655f14610f0957806360c8d12214610f7757610717565b80634fdd20f11161050557806354b8c601116104df57806354b8c60114610ecc5780635508d8e114610ed457806356342d8c14610edc5780635aa6e67514610ef957610717565b80634fdd20f114610ca657806352b8115d14610d6d5780635325e14414610e0557610717565b80634b341aed116105415780634b341aed14610c3e5780634c02550014610c645780634e3c83f114610c965780634fd101d714610c9e57610717565b806341b3d18514610bf2578063451b598514610bfa57806348ab8b2a14610c0257610717565b80632079fb9a1161064c5780632fa9d18b116105ea57806335aa2e44116105b957806335aa2e4414610ac85780633862da0b14610b8157806339610f7814610ba75780633c34355614610bc457610717565b80632fa9d18b14610a0c57806330cbe57114610a9b57806331c2273b14610ab85780633427458614610ac057610717565b80632649263a116106265780632649263a146108f257806328cc4e41146109235780632cf44a431461094e5780632e17de78146109ef57610717565b80632079fb9a1461089757806325316411146108b457806325726df2146108bc57610717565b8063174e6832116106b95780631ae4818f116106935780631ae4818f1461082f5780631c404cec146108375780631dd6b9b1146108545780631e9b12ef1461087157610717565b8063174e6832146107e4578063178c2c831461080a57806317c2b9101461082757610717565b80630e15561a116106f55780630e15561a146107935780630ebb172a1461079b57806316827b1b146107a35780631705a3bd146107c057610717565b806306cfb1041461071c578063078a13b11461073b5780630cccfc5814610779575b600080fd5b6107396004803603602081101561073257600080fd5b503561187c565b005b6107396004803603608081101561075157600080fd5b506001600160a01b038135811691602081013582169160408201358116916060013516611889565b610781611901565b60408051918252519081900360200190f35b610781611907565b61078161190d565b610739600480360360208110156107b957600080fd5b5035611913565b6107c861199c565b604080516001600160a01b039092168252519081900360200190f35b610781600480360360208110156107fa57600080fd5b50356001600160a01b03166119ab565b6107396004803603602081101561082057600080fd5b5035611a35565b610781611cc2565b6107c8611cc8565b6107396004803603602081101561084d57600080fd5b5035611cd7565b6107396004803603602081101561086a57600080fd5b5035611cf0565b6107396004803603602081101561088757600080fd5b50356001600160a01b0316611cfd565b6107c8600480360360208110156108ad57600080fd5b5035611d3a565b610781611d61565b6108d9600480360360208110156108d257600080fd5b5035611d67565b6040805192835260208301919091528051918290030190f35b61090f6004803603602081101561090857600080fd5b5035611d80565b604080519115158252519081900360200190f35b6107396004803603606081101561093957600080fd5b50803590602081013590604001351515611dbb565b6107396004803603602081101561096457600080fd5b810190602081018135600160201b81111561097e57600080fd5b82018360208201111561099057600080fd5b803590602001918460208302840111600160201b831117156109b157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611de0945050505050565b61073960048036036020811015610a0557600080fd5b5035611e04565b610781600480360360a0811015610a2257600080fd5b8135916020810135916040820135916001600160a01b036060820135169181019060a081016080820135600160201b811115610a5d57600080fd5b820183602082011115610a6f57600080fd5b803590602001918460608302840111600160201b83111715610a9057600080fd5b509092509050611e19565b61073960048036036020811015610ab157600080fd5b503561212a565b6107c861213f565b61078161214e565b610ae560048036036020811015610ade57600080fd5b5035612154565b604051808e81526020018d81526020018c81526020018b81526020018a8152602001896001600160a01b03166001600160a01b03168152602001886001600160a01b03166001600160a01b03168152602001876003811115610b4357fe5b60ff1681526020018681526020018581526020018481526020018381526020018281526020019d505050505050505050505050505060405180910390f35b61078160048036036020811015610b9757600080fd5b50356001600160a01b03166121c5565b61078160048036036020811015610bbd57600080fd5b50356121d7565b61073960048036036040811015610bda57600080fd5b506001600160a01b0381358116916020013516612238565b6107816122f1565b6107816122f7565b61073960048036036080811015610c1857600080fd5b508035906001600160a01b036020820135811691604081013590911690606001356122fd565b61078160048036036020811015610c5457600080fd5b50356001600160a01b03166123a3565b61090f60048036036060811015610c7a57600080fd5b50803590602081013590604001356001600160a01b03166124da565b6107816124fb565b610781612501565b610739600480360360a0811015610cbc57600080fd5b6001600160a01b0382351691602081013591604082013591606081013515159181019060a081016080820135600160201b811115610cf957600080fd5b820183602082011115610d0b57600080fd5b803590602001918460018302840111600160201b83111715610d2c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550612507945050505050565b610739600480360360c0811015610d8357600080fd5b6001600160a01b038235169160208101359160408201359160608101359160808201351515919081019060c0810160a0820135600160201b811115610dc757600080fd5b820183602082011115610dd957600080fd5b803590602001918460018302840111600160201b83111715610dfa57600080fd5b509092509050612525565b610e2260048036036020811015610e1b57600080fd5b5035612648565b60405180868152602001858152602001846001600160a01b03166001600160a01b031681526020018315151515815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610e8d578181015183820152602001610e75565b50505050905090810190601f168015610eba5780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390f35b61090f61270e565b610781612717565b6107c860048036036020811015610ef257600080fd5b503561271d565b6107c861273b565b6107c861274f565b61078160048036036020811015610f1f57600080fd5b810190602081018135600160201b811115610f3957600080fd5b820183602082011115610f4b57600080fd5b803590602001918460018302840111600160201b83111715610f6c57600080fd5b50909250905061275f565b61073960048036036060811015610f8d57600080fd5b5080359060208101359060400135612766565b6107c860048036036020811015610fb657600080fd5b50356127cf565b61073960048036036040811015610fd357600080fd5b506001600160a01b03813516906020013561281b565b61073960048036036060811015610fff57600080fd5b813591602081013591810190606081016040820135600160201b81111561102557600080fd5b82018360208201111561103757600080fd5b803590602001918460018302840111600160201b8311171561105857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550612832945050505050565b61090f600480360360608110156110af57600080fd5b50803590602081013590604001356001600160a01b0316612978565b610781612990565b610739612996565b6107816129f1565b6107816129f7565b6107396004803603602081101561110157600080fd5b50356129fd565b6107816004803603602081101561111e57600080fd5b50356001600160a01b0316612a16565b6107c8612a28565b610781612a37565b610781612a3d565b6107816004803603602081101561115c57600080fd5b5035612a43565b6107816004803603602081101561117957600080fd5b5035612a9a565b610781612aaf565b610739600480360360a081101561119e57600080fd5b6001600160a01b0382351691602081013591604082013591606081013515159181019060a081016080820135600160201b8111156111db57600080fd5b8201836020820111156111ed57600080fd5b803590602001918460018302840111600160201b8311171561120e57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550612ab5945050505050565b610781612acc565b6107396004803603604081101561126d57600080fd5b5080359060200135612ad2565b6107396004803603602081101561129057600080fd5b5035612b18565b6107c8612b29565b6107c8612b3d565b61090f612b4c565b610781612b8f565b610739600480360360208110156112cd57600080fd5b5035612b95565b610781612bab565b610739600480360360208110156112f257600080fd5b5035612bb1565b6107396004803603606081101561130f57600080fd5b50803590602081013590604001351515612bc6565b6107c8612be5565b6107396004803603604081101561134257600080fd5b5080359060200135612bf4565b610781612c5a565b6107396004803603602081101561136d57600080fd5b5035612c60565b6107396004803603604081101561138a57600080fd5b5080359060200135612d1c565b610739600480360360408110156113ad57600080fd5b5080359060200135612d82565b610739600480360360208110156113d057600080fd5b5035612e75565b610781612e8c565b610739600480360360808110156113f557600080fd5b813591602081013591604082013515159190810190608081016060820135600160201b81111561142457600080fd5b82018360208201111561143657600080fd5b803590602001918460018302840111600160201b8311171561145757600080fd5b509092509050612e92565b610739612f43565b610781612f55565b610781612f5b565b6107396004803603604081101561149057600080fd5b506001600160a01b038135169060200135612f61565b610739600480360360408110156114bc57600080fd5b5080359060200135612f75565b610781600480360360208110156114df57600080fd5b5035612f88565b61090f600480360360608110156114fc57600080fd5b50803590602081013590604001356001600160a01b0316612fd6565b6107396004803603604081101561152e57600080fd5b50803590602001356001600160a01b0316612fed565b61090f6004803603606081101561155a57600080fd5b50803590602081013590604001356001600160a01b03166130a8565b6107396004803603602081101561158c57600080fd5b50356130b7565b610781613140565b61090f613146565b610739600480360360208110156115b957600080fd5b5035151561314f565b610781600480360360208110156115d857600080fd5b50356132a1565b610739600480360360208110156115f557600080fd5b50356132b3565b6107396004803603604081101561161257600080fd5b50803590602001356132c8565b610781613330565b6108d9613336565b6107396004803603602081101561164557600080fd5b503561333f565b6107396004803603602081101561166257600080fd5b50356001600160a01b03166133fb565b6107816004803603602081101561168857600080fd5b503561347b565b6107c861348d565b6107c861349c565b610739600480360360208110156116b557600080fd5b503515156134ab565b610739600480360360208110156116d457600080fd5b50356001600160a01b03166134c6565b610739600480360360408110156116fa57600080fd5b81359190810190604081016020820135600160201b81111561171b57600080fd5b82018360208201111561172d57600080fd5b803590602001918460018302840111600160201b8311171561174e57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506134e0945050505050565b61073960048036036101208110156117a657600080fd5b506001600160a01b038135811691602081013582169160408201358116916060810135821691608082013581169160a081013582169160c082013581169160e081013582169161010090910135166136e5565b6107396004803603602081101561180f57600080fd5b5035613888565b6107396138a3565b6107c86138b3565b6107396004803603606081101561183c57600080fd5b50803590602081013590604001356138c2565b6107816139dd565b6107c86139e3565b6107396004803603602081101561187557600080fd5b50356139f2565b611884613a07565b600f55565b611891613a07565b61189a81613a55565b6118a357600080fd5b60238054610100600160a81b0319169055602580546001600160a01b03199081166001600160a01b03938416179091556006805482169583169590951790945560058054851693821693909317909255600780549093169116179055565b60155481565b60135481565b60085481565b61191b613a07565b8061192557600080fd5b60055460105460408051630f1980a560e41b8152600481018590526024810192909252516001600160a01b039092169163f1980a509160448082019260009290919082900301818387803b15801561197c57600080fd5b505af1158015611990573d6000803e3d6000fd5b50505060109190915550565b602c546001600160a01b031681565b60065460408051632f745c5960e01b81526001600160a01b038481166004830152600060248301819052925192931691632f745c5991604480820192602092909190829003018186803b158015611a0157600080fd5b505afa158015611a15573d6000803e3d6000fd5b505050506040513d6020811015611a2b57600080fd5b505190505b919050565b80611a3f81613a74565b60026000838152601a6020526040902060060154600160a01b900460ff166003811115611a6857fe5b14611aa7576040805162461bcd60e51b815260206004820152600a602482015269139bdd081a985a5b195960b21b604482015290519081900360640190fd5b6000828152601a602052604090206003015415611aff576040805162461bcd60e51b8152602060048201526011602482015270416c726561647920756e7374616b696e6760781b604482015290519081900360640190fd5b6009546000838152601a6020526040902060040154811015611b61576040805162461bcd60e51b8152602060048201526016602482015275125b98dbdb5c1b195d19481a985a5b081c195c9a5bd960521b604482015290519081900360640190fd5b6000838152601a6020526040902054600c54811015611b7f57600080fd5b6000848152601a60205260409020600601546001600160a01b03168015611bf457806001600160a01b031663a69df4b56040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611bdb57600080fd5b505af1158015611bef573d6000803e3d6000fd5b505050505b6000858152601a60205260409020600a0154611c2490611c1b90849063ffffffff613afe16565b60016000613b10565b6000858152601a602052604080822060068101805460ff60a01b1916600160a01b1790556005908101549054825163f92ec5af60e01b8152600481018a90526001600160a01b039283166024820181905293519394929091169263f92ec5af9260448084019391929182900301818387803b158015611ca257600080fd5b505af1158015611cb6573d6000803e3d6000fd5b50505050505050505050565b60175481565b6007546001600160a01b031681565b80611ce181613a74565b611cec826001613bcc565b5050565b611cf8613a07565b600955565b611d05613a07565b6001600160a01b038116611d1857600080fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b60268181548110611d4757fe5b6000918252602090912001546001600160a01b0316905081565b600e5481565b601e602052600090815260409020805460019091015482565b6000818152601a6020526040812060068101548154600390920154600954611db593600160a01b90930460ff16929190613be0565b92915050565b611dc3613c17565b82611dcd81613a74565b611dda8484846000613c58565b50505050565b611de8612b4c565b611df157600080fd5b8051611cec906026906020840190615e39565b80611e0e81613a74565b611cec826000613e32565b6002546000906001600160a01b03163314611e3357600080fd5b600954601d546000908190611e46615e9e565b6026546040805191840180835260208082028401019091528015611e74578160200160208202803883390190505b5060408083019190915260268054825160208083028201810190945281815292830182828015611ecd57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611eaf575b50505050506060820152600060208201526026546080820152611eee615ecd565b6026546040805191850180835260208082028401019091528015611f1c578160200160208202803883390190505b50602082015260005b888110156120eb576000611f738e8c8c85818110611f3f57fe5b9050606002016003806020026040519081016040528092919082600360200280828437600092019190915250613ee5915050565b9050856001600160a01b0316816001600160a01b03161415611f9557506120e3565b856001600160a01b0316816001600160a01b03161015611fb557506120eb565b6001600160a01b0381166000908152601b6020908152604080832054808452601a909252909120805460068201546003909201548087529091600160a01b900460ff169061200790829084908e613be0565b15612092576000838152601a60205260409020600a0154939850889361204690839061203a908d9063ffffffff613afe16565b9063ffffffff613afe16565b8651909a50156120805782866020015187604001518151811061206557fe5b6020908102919091010152604086018051600101905261208d565b61208a8785613fe8565b96505b6120de565b60028160038111156120a057fe5b14156120de578287604001518860000151815181106120bb57fe5b602090810291909101810191909152875160019081018952908801805190910190525b505050505b600101611f25565b506120f7826000613fe8565b91506121198d8b878e86604001518760000151876020015188604001516140c5565b9d9c50505050505050505050505050565b8061213481613a74565b611cec826001613e32565b6006546001600160a01b031681565b60165481565b601a60205260009081526040902080546001820154600283015460038401546004850154600586015460068701546007880154600889015460098a0154600a8b0154600b909b0154999a98999798969795966001600160a01b039586169695851695600160a01b90950460ff16948d565b601b6020526000908152604090205481565b6000818152601a602052604081206003015481906121fc576121f88361422d565b9150505b6000838152601a602052604090206009015461223190600190612225908463ffffffff613afe16565b9063ffffffff6142a916565b9392505050565b612240613a07565b60038054602b80546001600160a01b038084166001600160a01b0319928316179283905592811686841617909355602c805490931684831617909255604080516370a0823160e01b81523060048201529051611cec93909216916370a0823191602480820192602092909190829003018186803b1580156122c057600080fd5b505afa1580156122d4573d6000803e3d6000fd5b505050506040513d60208110156122ea57600080fd5b50516142be565b600c5481565b60295481565b612305613a07565b6000848152601a60205260409020600601546001600160a01b03168061232a57600080fd5b6040805163abf59fc960e01b81526001600160a01b03868116600483015285811660248301526044820185905291519183169163abf59fc99160648082019260009290919082900301818387803b15801561238457600080fd5b505af1158015612398573d6000803e3d6000fd5b505050505050505050565b60006001600160a01b03821615806124315750600654604080516370a0823160e01b81526001600160a01b038581166004830152915191909216916370a08231916024808301926020929190829003018186803b15801561240357600080fd5b505afa158015612417573d6000803e3d6000fd5b505050506040513d602081101561242d57600080fd5b5051155b1561243e57506000611a30565b60065460408051632f745c5960e01b81526001600160a01b0385811660048301526000602483018190529251601a949190911691632f745c59916044808301926020929190829003018186803b15801561249757600080fd5b505afa1580156124ab573d6000803e3d6000fd5b505050506040513d60208110156124c157600080fd5b5051815260208101919091526040016000205492915050565b6000836124e68161445d565b6124f2848460016144cb565b95945050505050565b600f5481565b60105481565b61250f613c17565b61251e85858585856000614571565b5050505050565b333014612567576040805162461bcd60e51b815260206004820152600b60248201526a1b9bdd08185b1b1bddd95960aa1b604482015290519081900360640190fd5b61257687888860006001614623565b6125848560095460016146f0565b60006125c888868686868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061485492505050565b6005546040805163e12ab1af60e01b815260048101849052602481018a90526044810189905290519293506001600160a01b039091169163e12ab1af9160648082019260009290919082900301818387803b15801561262657600080fd5b505af115801561263a573d6000803e3d6000fd5b505050505050505050505050565b60208080526000918252604091829020805460018083015460028085015460038601805489516101009682161596909602600019011692909204601f8101889004880285018801909852878452939691956001600160a01b03851695600160a01b90950460ff16949392908301828280156127045780601f106126d957610100808354040283529160200191612704565b820191906000526020600020905b8154815290600101906020018083116126e757829003601f168201915b5050505050905085565b60195460ff1681565b60125481565b6000908152601a60205260409020600601546001600160a01b031690565b60005461010090046001600160a01b031681565b6004546001600160a01b03165b90565b6000806000fd5b61276e613a07565b60255460408051602481018690526044810185905260648082018590528251808303909101815260849091019091526020810180516001600160e01b0316633064689160e11b1790526127ca916001600160a01b031690614c61565b505050565b600654604080516331a9108f60e11b81526004810184905290516000926001600160a01b031691636352211e916024808301926020929190829003018186803b158015611a0157600080fd5b612823613c17565b611cec82338360006001614623565b6017546040805133602080830191909152818301879052825180830384018152606090920190925280519101206128719184908463ffffffff614c8916565b6128b4576040805162461bcd60e51b815260206004820152600f60248201526e2bb937b7339030b1b190383937b7b360891b604482015290519081900360640190fd5b336000908152601f60205260408120546128d590859063ffffffff6142a916565b6022549091506128eb908263ffffffff6142a916565b6022556005546040805163122b648160e01b81523360048201526024810184905290516001600160a01b039092169163122b64819160448082019260009290919082900301818387803b15801561294157600080fd5b505af1158015612955573d6000803e3d6000fd5b5050336000818152601f60205260408120889055611dda93509091508390614ddf565b6000836129848161445d565b6124f2848460006144cb565b600a5481565b61299e612b4c565b6129a757600080fd5b6001546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b60095481565b60185481565b612a05613a07565b612a138160095460016146f0565b50565b601f6020526000908152604090205481565b6004546001600160a01b031681565b602a5481565b600b5481565b600081612a4f8161445d565b612a5883614ec9565b6000838152601a6020526040812060090154612a7b90600163ffffffff6142a916565b6000858152601a60205260409020600160099091015592505050919050565b6000908152601a60205260409020600a015490565b601d5490565b612abd613c17565b61251e85858585856001614571565b60115481565b81612adc8161445d565b6000838152601a60205260409020600a0154612afe908363ffffffff6142a916565b6000938452601a6020526040909320600a01929092555050565b612b20613a07565b612a13816142be565b60235461010090046001600160a01b031681565b6001546001600160a01b031690565b604080517f6d617469632e6e6574776f726b2e70726f78792e6f776e6572000000000000008152905190819003601901902054336001600160a01b039091161490565b60095490565b612b9d613a07565b612a138160095460006146f0565b60275481565b80612bbb81613a74565b611cec826000613bcc565b612bce613c17565b82612bd881613a74565b611dda8484846001613c58565b6002546001600160a01b031681565b612bfc613c17565b602554604080516024810185905260448101849052306064808301919091528251808303909101815260849091019091526020810180516001600160e01b031663561ad5c960e11b179052611cec916001600160a01b031690614c61565b60225481565b612c68613a07565b6005546016546040805163a3b1d8cb60e01b8152600481018590526024810192909252516001600160a01b039092169163a3b1d8cb9160448082019260009290919082900301818387803b158015612cbf57600080fd5b505af1158015612cd3573d6000803e3d6000fd5b505050506064811115612d17576040805162461bcd60e51b8152602060048201526007602482015266746f6f2062696760c81b604482015290519081900360640190fd5b601655565b612d24612b4c565b612d2d57600080fd5b602554604080516024810185905260448082018590528251808303909101815260649091019091526020810180516001600160e01b0316639ddbbf8560e01b179052611cec916001600160a01b031690614c61565b81612d8c8161445d565b6000821315612de55760195460ff16612de5576040805162461bcd60e51b815260206004820152601660248201527511195b1959d85d1a5bdb881a5cc8191a5cd8589b195960521b604482015290519081900360640190fd5b6000838152601a602052604090206003015480612e0d57612e0883600080613b10565b612e50565b600954811115612e50576040805162461bcd60e51b8152602060048201526009602482015268756e7374616b696e6760b81b604482015290519081900360640190fd5b60008312612e6757612e628484614ed8565b611dda565b611dda848460001902612ad2565b612e7d613a07565b80612e8757600080fd5b600e55565b601c5490565b612e9a613c17565b6025546040516024810187815260448201879052851515606483015260806084830190815260a4830185905261251e936001600160a01b03169263a685487760e01b928a928a928a928a928a9260c40184848082843760008184015260408051601f19601f9093018316909401848103909201845252506020810180516001600160e01b03199a909a166001600160e01b03909a169990991790985250614c6195505050505050565b612f4b613a07565b612f53614f13565b565b60085490565b60245481565b612f69613a07565b611cec82826001614ddf565b612f7d613a07565b600c91909155600d55565b6000818152601a60205260408120600301548190612fad57612fa98361422d565b5090505b6000838152601a602052604090206001908101546122319190612225908463ffffffff613afe16565b6000612fe58484846000614f1f565b949350505050565b612ff5613a07565b306001600160a01b0316816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561303857600080fd5b505afa15801561304c573d6000803e3d6000fd5b505050506040513d602081101561306257600080fd5b50516001600160a01b03161461307757600080fd5b6000918252601a602052604090912060060180546001600160a01b0319166001600160a01b03909216919091179055565b6000612fe58484846001614f1f565b6130bf613a07565b806130c957600080fd5b600554600b5460408051632dbe9d3160e21b8152600481018590526024810192909252516001600160a01b039092169163b6fa74c49160448082019260009290919082900301818387803b15801561312057600080fd5b505af1158015613134573d6000803e3d6000fd5b505050600b9190915550565b60145481565b60005460ff1681565b336000908152601b6020908152604080832054808452601a9092529091206006810154815460039092015460095461319493600160a01b90930460ff16929190613be0565b6131d5576040805162461bcd60e51b815260206004820152600d60248201526c3737ba103b30b634b230ba37b960991b604482015290519081900360640190fd5b6000818152601a60205260409020600601546001600160a01b03168061323b576040805162461bcd60e51b815260206004820152601660248201527511195b1959d85d1a5bdb881a5cc8191a5cd8589b195960521b604482015290519081900360640190fd5b604080516303dd464160e51b8152841515600482015290516001600160a01b03831691637ba8c82091602480830192600092919082900301818387803b15801561328457600080fd5b505af1158015613298573d6000803e3d6000fd5b50505050505050565b60216020526000908152604090205481565b806132bd81613a74565b611cec8260006150ae565b816132d281613a74565b6132db83614ec9565b602554604080516024810186905260448082018690528251808303909101815260649091019091526020810180516001600160e01b0316636e6cb15960e11b1790526127ca916001600160a01b031690614c61565b60285481565b601c54601d5482565b613347613a07565b6000811161335457600080fd5b600554600a5460408051635071805360e11b8152600481018590526024810192909252516001600160a01b039092169163a0e300a69160448082019260009290919082900301818387803b1580156133ab57600080fd5b505af11580156133bf573d6000803e3d6000fd5b505050600a8290555060088190556133de81600463ffffffff61528c16565b60158190556009546133f59163ffffffff613afe16565b60185550565b613403612b4c565b61340c57600080fd5b6001600160a01b03811661341f57600080fd5b6002546040516001600160a01b038084169216907f211c9015fc81c0dbd45bd99f0f29fc1c143bfd53442d5ffd722bbbef7a887fe990600090a3600280546001600160a01b0319166001600160a01b0392909216919091179055565b6000908152601a602052604090205490565b602b546001600160a01b031681565b6005546001600160a01b031681565b6134b3613a07565b6019805460ff1916911515919091179055565b6134ce612b4c565b6134d757600080fd5b612a13816152ae565b816134ea81613a74565b60006134f58361531d565b600954600f54600087815260216020526040902054929350909161351e9163ffffffff613afe16565b811015613560576040805162461bcd60e51b815260206004820152600b60248201526a139bdd08185b1b1bddd95960aa1b604482015290519081900360640190fd5b6000858152601a602090815260408083206005908101549054915163b80fbce560e01b8152600481018a81526001600160a01b039283166024830181905288841660448401526080606484019081528b5160848501528b519197949095169563b80fbce5958d9589958c958f95909460a490920192918601918190849084905b838110156135f85781810151838201526020016135e0565b50505050905090810190601f1680156136255780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b15801561364757600080fd5b505af115801561365b573d6000803e3d6000fd5b5050506000878152601a60205260409020600301541515905061368a57613681816153d6565b61368a836154e0565b6001600160a01b039081166000908152601b60209081526040808320600019905594909216808252848220889055968152601a825283812060050180546001600160a01b031916909717909655602190529320929092555050565b60235460ff161561372e576040805162461bcd60e51b815260206004820152600e60248201526d185b1c9958591e481a5b9a5d195960921b604482015290519081900360640190fd5b6023805460ff1916600117905561374481613a55565b61378e576040805162461bcd60e51b8152602060048201526016602482015275185d58dd1a5bdb881a5b5c1b081a5b98dbdc9c9958dd60521b604482015290519081900360640190fd5b602580546001600160a01b03199081166001600160a01b038481169190911790925560008054610100600160a81b031916610100878516021790556004805482168c84161790556002805482168b84161790556003805482168a84161790556006805482168984161790556005805482168884161790556007805490911691861691909117905561381e826152ae565b5050612000600855505060016009819055610376600a90815569044664c7bf6451f00000600b55670de0b6b3a7640000600c819055600d55610400600e556064600f55600760105560128290556108006015556016556019805460ff191690911790555050505050565b613890613a07565b6009546133f5908263ffffffff613afe16565b6138ab613a07565b612f536155fa565b6025546001600160a01b031681565b6007821161390b576040805162461bcd60e51b815260206004820152601160248201527024b73b30b634b21036b4b3b930ba34b7b760791b604482015290519081900360640190fd5b6000838152601a6020526040808220600601548151636e7ce59160e01b81523360048201526024810185905291516001600160a01b0390911692636e7ce591926044808201939182900301818387803b15801561396757600080fd5b505af115801561397b573d6000803e3d6000fd5b5050506000838152601a6020526040808220600601548151632830728d60e21b81523360048201526024810186905291516001600160a01b03909116935063a0c1ca349260448084019391929182900301818387803b15801561328457600080fd5b600d5481565b6003546001600160a01b031681565b806139fc81613a74565b611cec8260016150ae565b60005461010090046001600160a01b03163314612f535760405162461bcd60e51b8152600401808060200182810382526026815260200180615f6b6026913960400191505060405180910390fd5b60006001600160a01b038216613a6d57506000611a30565b503b151590565b600654604080516331a9108f60e11b815260048101849052905133926001600160a01b031691636352211e916024808301926020929190829003018186803b158015613abf57600080fd5b505afa158015613ad3573d6000803e3d6000fd5b505050506040513d6020811015613ae957600080fd5b50516001600160a01b031614612a1357600080fd5b60008282018381101561223157600080fd5b80613baa576000831315613b3957601c54613b31908463ffffffff613afe16565b601c55613b5d565b6000831215613b5d57601c54613b5990600085900363ffffffff6142a916565b601c555b6000821315613b8157601d54613b79908363ffffffff613afe16565b601d55613ba5565b6000821215613ba557601d54613ba190600084900363ffffffff6142a916565b601d555b6127ca565b6000818152601e60205260409020805484018155600101805483019055505050565b613bd582614ec9565b611cec823383615609565b60008084118015613bf85750821580613bf857508183115b80156124f257506001856003811115613c0d57fe5b1495945050505050565b60005460ff1615612f53576040805162461bcd60e51b81526020600482015260066024820152651b1bd8dad95960d21b604482015290519081900360640190fd5b6000848152601a602052604090206003015415613cab576040805162461bcd60e51b815260206004820152600c60248201526b4e6f2072657374616b696e6760a01b604482015290519081900360640190fd5b8215613cbd57613cbd333085846156e0565b613cc684614ec9565b8115613d0d576000848152601a60205260409020600190810154613cf6919061222590869063ffffffff613afe16565b6000858152601a6020526040902060019081015592505b601154600090613d23908563ffffffff613afe16565b60118190556000868152601a6020526040902054909150613d4a908563ffffffff613afe16565b6000868152601a6020526040812091909155613d6890859080613b10565b6005546040805163532e19a960e01b81526004810188905290516001600160a01b039092169163532e19a99160248082019260009290919082900301818387803b158015613db557600080fd5b505af1158015613dc9573d6000803e3d6000fd5b50506005546000888152601a6020526040808220548151631585a9f360e21b8152600481018c905260248101919091526044810187905290516001600160a01b039093169450635616a7cc93506064808201939182900301818387803b15801561238457600080fd5b600082815260208052604090205415613e4a57600080fd5b6000828152601a602052604090206006810154600290910154600160a01b90910460ff169015801590613e8c57506000838152601a6020526040902060030154155b8015613eb857506001816003811115613ea157fe5b1480613eb857506002816003811115613eb657fe5b145b613ec157600080fd5b600954600090613ed890600163ffffffff613afe16565b9050611dda8482856146f0565b805160208201516040830151600092919060ff167f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0821115613f2d5760009350505050611db5565b601b8160ff161015613f3d57601b015b8060ff16601b14158015613f5557508060ff16601c14155b15613f665760009350505050611db5565b60408051600080825260208083018085528a905260ff85168385015260608301879052608083018690529251909260019260a080820193601f1981019281900390910190855afa158015613fbe573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116613fde57600080fd5b9695505050505050565b613ff0615e9e565b826080015183602001511080156140345750816001600160a01b0316836060015184602001518151811061402057fe5b60200260200101516001600160a01b031614155b156140b557601b6000846060015185602001518151811061405157fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002054836040015184600001518151811061408e57fe5b60209081029190910181019190915283516001908101855290840180519091019052613ff0565b5050602081018051600101905290565b601c546000906140f2600161203a60036140e685600263ffffffff6157e116565b9063ffffffff61528c16565b88101561413c576040805162461bcd60e51b8152602060048201526013602482015272322f332b31206e6f6e2d6d616a6f726974792160681b604482015290519081900360640190fd5b60006141498b8a84615808565b9050600061416760646140e6601654856157e190919063ffffffff16565b6001600160a01b038c166000908152601b6020908152604080832054808452601a909252909120600181015492935090916141a8908463ffffffff613afe16565b600182015560178b905560006141f56141e68e6140e66a084595161401484a0000006141da8a8a63ffffffff6142a916565b9063ffffffff6157e116565b6024549063ffffffff613afe16565b90506142028b8b8361595c565b602481905561421289898361595c565b61421a615990565b50929d9c50505050505050505050505050565b6000818152601a602052604081208054600a90910154829190829061425990839063ffffffff613afe16565b6000868152601a60205260409020600b0154602454919250900361429d86846142976a084595161401484a0000006140e6868863ffffffff6157e116565b85615a05565b94509450505050915091565b6000828211156142b857600080fd5b50900390565b602b54604080516370a0823160e01b8152306004820152905183926001600160a01b0316916370a08231916024808301926020929190829003018186803b15801561430857600080fd5b505afa15801561431c573d6000803e3d6000fd5b505050506040513d602081101561433257600080fd5b50511015614377576040805162461bcd60e51b815260206004820152600d60248201526c4c61636b696e67204d4154494360981b604482015290519081900360640190fd5b602b54602c546040805163095ea7b360e01b81526001600160a01b039283166004820152602481018590529051919092169163095ea7b39160448083019260209291908290030181600087803b1580156143d057600080fd5b505af11580156143e4573d6000803e3d6000fd5b505050506040513d60208110156143fa57600080fd5b5050602c54604080516308a960c160e31b81526004810184905290516001600160a01b039092169163454b06089160248082019260009290919082900301818387803b15801561444957600080fd5b505af115801561251e573d6000803e3d6000fd5b6000818152601a60205260409020600601546001600160a01b03163314612a13576040805162461bcd60e51b815260206004820152601860248201527f496e76616c696420636f6e747261637420616464726573730000000000000000604482015290519081900360640190fd5b6000806144d783615a99565b604080516323b872dd60e01b81526001600160a01b038781166004830152306024830152604482018990529151929350600092918416916323b872dd9160648082019260209290919082900301818787803b15801561453557600080fd5b505af1158015614549573d6000803e3d6000fd5b505050506040513d602081101561455f57600080fd5b50519050836124f2576124f2866142be565b60105461457c612aaf565b106145be576040805162461bcd60e51b815260206004820152600d60248201526c6e6f206d6f726520736c6f747360981b604482015290519081900360640190fd5b600c5485101561460a576040805162461bcd60e51b81526020600482015260126024820152711b9bdd08195b9bdd59da0819195c1bdcda5d60721b604482015290519081900360640190fd5b6146178633868885614623565b61329886868585614854565b600d5483101561466a576040805162461bcd60e51b815260206004820152600d60248201526c199959481d1bdbc81cdb585b1b609a1b604482015290519081900360640190fd5b614685843061467f868663ffffffff613afe16565b846156e0565b602254614698908463ffffffff613afe16565b6022556005546040805163a449d79560e01b81526001600160a01b038881166004830152602482018790529151919092169163a449d79591604480830192600092919082900301818387803b15801561238457600080fd5b6146f983614ec9565b6000838152601a602052604081205490614712856127cf565b6000868152601a6020526040902060038101869055600a810154600690910154919250906001600160a01b0316801561479957806001600160a01b031663f83d08ba6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561478057600080fd5b505af1158015614794573d6000803e3d6000fd5b505050505b6000878152601a60205260409020600501546147bd906001600160a01b03166153d6565b6147c8878487615609565b60006009548711156147da57866147dd565b60005b90506147f183860160000360001983613b10565b60055460408051635e04d48360e01b81526001600160a01b038781166004830152602482018c9052604482018b90526064820189905291519190921691635e04d48391608480830192600092919082900301818387803b15801561262657600080fd5b6000806148608361531d565b600954601254600554601154939450919290916001600160a01b031690600090614890908a63ffffffff613afe16565b905080601181905550604051806101a001604052808a8152602001600181526020018581526020016000815260200160008152602001866001600160a01b03168152602001896148e157600061496d565b600754600480546040805163d4ad267360e01b81529283018990526001600160a01b03888116602485015291821660448401525192169163d4ad2673916064808201926020929091908290030181600087803b15801561494057600080fd5b505af1158015614954573d6000803e3d6000fd5b505050506040513d602081101561496a57600080fd5b50515b6001600160a01b031681526020016001815260006020808301829052604080840183905260016060808601829052608080870186905260245460a0978801528a8652601a855294839020875181559387015191840191909155908501516002830155840151600380830191909155918401516004820155918301516005830180546001600160a01b039283166001600160a01b03199182161790915560c08501516006850180549190931691161780825560e08501519260ff60a01b1990911690600160a01b908490811115614a3f57fe5b0217905550610100820151600782015561012082015160088201556101408201516009820155610160820151600a82015561018090910151600b9091015560008381526021602052604080822086905560065481516340c10f1960e01b81526001600160a01b038e8116600483015260248201889052925192909116926340c10f199260448084019382900301818387803b158015614add57600080fd5b505af1158015614af1573d6000803e3d6000fd5b5050506001600160a01b0386166000908152601b60205260408120859055614b1e91508a90600190613b10565b836020600085815260200190815260200160002060010181905550816001600160a01b03166333a8383c868986888e876040518763ffffffff1660e01b815260040180876001600160a01b03166001600160a01b0316815260200180602001868152602001858152602001848152602001838152602001828103825287818151815260200191508051906020019080838360005b83811015614bca578181015183820152602001614bb2565b50505050905090810190601f168015614bf75780820380516001836020036101000a031916815260200191505b50975050505050505050600060405180830381600087803b158015614c1b57600080fd5b505af1158015614c2f573d6000803e3d6000fd5b50505050614c47600184613afe90919063ffffffff16565b601255614c53856154e0565b509098975050505050505050565b600080825160208401856127105a03f43d604051816000823e828015614c85578282f35b8282fd5b60006020825181614c9657fe5b0615614ce0576040805162461bcd60e51b8152602060048201526014602482015273092dcecc2d8d2c840e0e4dedecc40d8cadccee8d60631b604482015290519081900360640190fd5b60006020835181614ced57fe5b0490508060020a8510614d3f576040805162461bcd60e51b81526020600482015260156024820152744c65616620696e64657820697320746f6f2062696760581b604482015290519081900360640190fd5b60008660205b85518111614dd15785810151925060028806614d915781836040516020018083815260200182815260200192505050604051602081830303815290604052805190602001209150614dc3565b828260405160200180838152602001828152602001925050506040516020818303038152906040528051906020012091505b600288049750602001614d45565b509094149695505050505050565b80614ded57614ded82615ac2565b6000614df882615a99565b9050806001600160a01b031663a9059cbb85856040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015614e5a57600080fd5b505af1158015614e6e573d6000803e3d6000fd5b505050506040513d6020811015614e8457600080fd5b5051611dda576040805162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015290519081900360640190fd5b612a1381602454602454615c4b565b6000828152601a60205260409020600a0154614efa908263ffffffff613afe16565b6000928352601a6020526040909220600a019190915550565b6000805460ff19169055565b6000848152601a60205260408120600601546001600160a01b0316331480614fbe57506004805460408051631ab0168360e31b8152905133936001600160a01b039093169263d580b41892808201926020929091829003018186803b158015614f8757600080fd5b505afa158015614f9b573d6000803e3d6000fd5b505050506040513d6020811015614fb157600080fd5b50516001600160a01b0316145b614ffd576040805162461bcd60e51b815260206004820152600b60248201526a1b9bdd08185b1b1bddd95960aa1b604482015290519081900360640190fd5b8161500b5761500b84615ac2565b600061501683615a99565b9050806001600160a01b031663a9059cbb85876040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561507857600080fd5b505af115801561508c573d6000803e3d6000fd5b505050506040513d60208110156150a257600080fd5b50519695505050505050565b6000828152601a602052604090206003015480158015906150e457506009546008546150e190839063ffffffff613afe16565b11155b8015615117575060036000848152601a6020526040902060060154600160a01b900460ff16600381111561511457fe5b14155b61512057600080fd5b6000838152601a6020526040812054601154909190615145908363ffffffff6142a916565b60118190559050615157853386615609565b60065460408051630852cd8d60e31b81526004810188905290516001600160a01b03909216916342966c689160248082019260009290919082900301818387803b1580156151a457600080fd5b505af11580156151b8573d6000803e3d6000fd5b5050506000868152601a6020526040812081815560048101919091556005810180546001600160a01b03191690556000197f584f46c60af19681376031579adb04a2416e54ee5505351c2a8435e3766026ea55600601805460ff60a01b1916600360a01b1790555061522b338386614ddf565b6005546040805163ae2e26b160e01b815233600482015260248101889052604481018590526064810184905290516001600160a01b039092169163ae2e26b19160848082019260009290919082900301818387803b15801561238457600080fd5b600080821161529a57600080fd5b60008284816152a557fe5b04949350505050565b6001600160a01b0381166152c157600080fd5b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000815160401461535f576040805162461bcd60e51b81526020600482015260076024820152663737ba10383ab160c91b604482015290519081900360640190fd5b815160208301206001600160a01b0381161580159061539457506001600160a01b0381166000908152601b6020526040902054155b611db5576040805162461bcd60e51b815260206004820152600e60248201526d24b73b30b634b21039b4b3b732b960911b604482015290519081900360640190fd5b602680549060009060001983018381106153ec57fe5b600091825260209091200154602680546001600160a01b03909216925090600019840190811061541857fe5b600091825260209091200180546001600160a01b031916905560001982015b80156154cf57836001600160a01b0316826001600160a01b0316141561545c576154cf565b6026600182038154811061546c57fe5b9060005260206000200160009054906101000a90046001600160a01b0316826026600184038154811061549b57fe5b600091825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055915060001901615437565b506000198201611dda602682615eee565b6026805460018101825560008290527f744a2cf8fd7008e3d53b67916e73460df9fa5214e3ef23dd4259ca09493a35940180546001600160a01b0319166001600160a01b0384161790555460001901805b80156155b25760006026600183038154811061554957fe5b6000918252602090912001546001600160a01b039081169150841681101561557157506155b2565b806026838154811061557f57fe5b600091825260209091200180546001600160a01b0319166001600160a01b03929092169190911790555060001901615531565b8181146127ca5782602682815481106155c757fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550505050565b6000805460ff19166001179055565b6000838152601a6020526040812060019081015461562c9163ffffffff6142a916565b601454909150615642908263ffffffff613afe16565b6014556000848152601a60205260409020600190810155615664838284614ddf565b60055460145460408051635b42d93560e11b815260048101889052602481018590526044810192909252516001600160a01b039092169163b685b26a9160648082019260009290919082900301818387803b1580156156c257600080fd5b505af11580156156d6573d6000803e3d6000fd5b5050505050505050565b60006156eb82615a99565b604080516323b872dd60e01b81526001600160a01b0388811660048301528781166024830152604482018790529151929350908316916323b872dd916064808201926020929091908290030181600087803b15801561574957600080fd5b505af115801561575d573d6000803e3d6000fd5b505050506040513d602081101561577357600080fd5b50516157bd576040805162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b604482015290519081900360640190fd5b811580156157d357506001600160a01b03841630145b1561251e5761251e836142be565b6000826157f057506000611db5565b828202828482816157fd57fe5b041461223157600080fd5b600e54600b54600091908261582883888161581f57fe5b04602954615d1b565b905060008111801561583c57506027548114155b1561587d57602754156158775760006064602a5484028161585957fe5b04905081602754111561586f5791820191615875565b80830392505b505b60278190555b600083881115615916576028546158c56158b560646140e66158a860026000198901890204866157e1565b889063ffffffff6157e116565b612225868663ffffffff6157e116565b91506158e76158da848763ffffffff6157e116565b8a9063ffffffff6142a916565b985061591261590560646140e6846141da898963ffffffff6157e116565b859063ffffffff6142a916565b9350505b61593a61592d856140e68b8763ffffffff6157e116565b829063ffffffff613afe16565b9050615950866140e6838a63ffffffff6157e116565b98975050505050505050565b60245460005b8381101561251e5761598885828151811061597957fe5b60200260200101518385615c4b565b600101615962565b60095460006159a682600163ffffffff613afe16565b90506159b0615f12565b506000818152601e602090815260408083208151808301909252805480835260019091015492820183905290926159e79290613b10565b506000918252601e6020526040822082815560010191909155600955565b60008082615a1857506000905080615a90565b6000615a2e846140e6888863ffffffff6157e116565b6000888152601a60205260409020600701549091508015615a7557615a72615a6560646140e6846141da8b8863ffffffff6142a916565b839063ffffffff613afe16565b91505b6000615a87878463ffffffff6142a916565b92945091925050505b94509492505050565b600081615ab157602b546001600160a01b0316611db5565b50506003546001600160a01b031690565b600354604080516370a0823160e01b8152306004820152905183926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015615b0c57600080fd5b505afa158015615b20573d6000803e3d6000fd5b505050506040513d6020811015615b3657600080fd5b50511015615b79576040805162461bcd60e51b815260206004820152600b60248201526a131858dada5b99c81413d360aa1b604482015290519081900360640190fd5b600354602c546040805163095ea7b360e01b81526001600160a01b039283166004820152602481018590529051919092169163095ea7b39160448083019260209291908290030181600087803b158015615bd257600080fd5b505af1158015615be6573d6000803e3d6000fd5b505050506040513d6020811015615bfc57600080fd5b5050602c546040805163efeff0c160e01b81526004810184905290516001600160a01b039092169163efeff0c19160248082019260009290919082900301818387803b15801561444957600080fd5b6000838152601a60205260409020600301548015801590615c6e57508060095410155b15615c7957506127ca565b6000848152601a60205260409020600b015483811015615cf8576000858152601a602052604090208054600a909101548015615ce0576000615cc1838363ffffffff613afe16565b9050615cda888484615cd58c868d8b615d31565b615d55565b50615cf5565b615cf587615cf089858a88615d31565b615dfb565b50505b8083111561251e576000858152601a60205260409020600b018390555050505050565b6000818310615d2a5781612231565b5090919050565b6000818303613fde6a084595161401484a0000006140e6838863ffffffff6157e116565b6000615d67838563ffffffff613afe16565b9050600080615d7887878686615a05565b90925090508015615db8576000878152601a6020526040902060090154615da5908263ffffffff613afe16565b6000888152601a60205260409020600901555b8115613298576000878152601a6020526040902060010154615de0908363ffffffff613afe16565b6000888152601a602052604090206001015550505050505050565b8015611cec576000828152601a6020526040902060010154615e23908263ffffffff613afe16565b6000838152601a60205260409020600101555050565b828054828255906000526020600020908101928215615e8e579160200282015b82811115615e8e57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190615e59565b50615e9a929150615f2c565b5090565b6040518060a0016040528060008152602001600081526020016060815260200160608152602001600081525090565b60405180606001604052806000815260200160608152602001600081525090565b8154818355818111156127ca576000838152602090206127ca918101908301615f50565b604051806040016040528060008152602001600081525090565b61275c91905b80821115615e9a5780546001600160a01b0319168155600101615f32565b61275c91905b80821115615e9a5760008155600101615f5656fe4f6e6c7920676f7665726e616e636520636f6e747261637420697320617574686f72697a6564a265627a7a723158202144f16fe19a38e4b93eaa1e2c04f33e2a9f779412c9defdf18d621af20c242664736f6c63430005110032", + "nonce": "0x8c", + "chainId": "0xaa36a7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xae07c932b4a5459bd683767dde161e9fa7ad1cc9b0ac83a39f9c291bcb22b79f", + "transactionType": "CREATE", + "contractName": null, + "contractAddress": "0x19b0b3256b22116096a2ef8cafc6e832ba18a947", + "function": null, + "arguments": null, + "transaction": { + "from": "0x0724d72eb61e508d81ca701881f2248f092953bf", + "gas": "0x3b3afb", + "value": "0x0", + "input": "0x60806040526003805460ff60a81b191690553480156200001e57600080fd5b5060038054610100338102610100600160a81b0319909216919091179182905560405191046001600160a01b0316906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3620000886001600160e01b036200008e16565b620000a3565b6003805460ff60a81b1916600160a81b179055565b61349680620000b36000396000f3fe608060405234801561001057600080fd5b50600436106103c55760003560e01c80638759c234116101ff578063b6658d071161011a578063e0db556b116100ad578063e97fddc21161007c578063e97fddc2146109aa578063eaf43bb5146109c7578063f2fde38b14610a05578063f83d08ba14610a2b576103c5565b8063e0db556b14610936578063e4457a8a1461093e578063e4cd1aec14610961578063e570b78b14610987576103c5565b8063c83ec04d116100e9578063c83ec04d146108d5578063cf309012146108f8578063dd62ed3e14610900578063df5cf7231461092e576103c5565b8063b6658d071461089a578063b89b63c1146108a2578063bfb18f29146108c5578063c7b8981c146108cd576103c5565b80639b2cb5d811610192578063a9059cbb11610161578063a9059cbb146107d8578063abf59fc914610804578063b4988fd01461083a578063b60c76fa1461086e576103c5565b80639b2cb5d814610770578063a0c1ca3414610778578063a457c2d7146107a4578063a69df4b5146107d0576103c5565b80638d16a14a116101ce5780638d16a14a146107505780638da5cb5b146107585780638f32d59b146107605780639ad5f5a414610768576103c5565b80638759c2341461071b578063883b455f146107385780638ccdd289146107405780638d086da414610748576103c5565b80634f91440d116102ef5780636ab1507111610282578063715018a611610251578063715018a6146106c05780637542ff95146106c8578063795be587146106d05780637ba8c820146106fc576103c5565b80636ab15071146106225780636cbb6050146106455780636e7ce5911461066e57806370a082311461069a576103c5565b80635f0c80cc116102be5780635f0c80cc146105a35780636035083f146105ab578063653ec134146105d6578063676e5550146105fc576103c5565b80634f91440d1461056c5780635adc6dde146105745780635c42c733146105935780635c5f7dae1461059b576103c5565b806323b872dd116103675780633a09bf44116103365780633a09bf44146105305780633ba0b9a9146105385780633d94eb0514610540578063446a2ec814610564576103c5565b806323b872dd146104a05780633046c204146104d657806339509351146104fc57806339c31e9314610528576103c5565b80631bf494a7116103a35780631bf494a7146104495780631e7ff8f614610451578063232e5415146104905780632344067914610498576103c5565b8063029d3040146103ca578063095ea7b3146103ef57806318160ddd1461042f575b600080fd5b6103ed600480360360408110156103e057600080fd5b5080359060200135610a33565b005b61041b6004803603604081101561040557600080fd5b506001600160a01b038135169060200135610a43565b604080519115158252519081900360200190f35b610437610a5a565b60408051918252519081900360200190f35b610437610a61565b6104776004803603602081101561046757600080fd5b50356001600160a01b0316610aed565b6040805192835260208301919091528051918290030190f35b6103ed610b4d565b610437610b59565b61041b600480360360608110156104b657600080fd5b506001600160a01b03813581169160208101359091169060400135610b5f565b610437600480360360208110156104ec57600080fd5b50356001600160a01b0316610bb7565b61041b6004803603604081101561051257600080fd5b506001600160a01b038135169060200135610bc9565b610437610c05565b610437610c0b565b610437610c11565b610548610cce565b604080516001600160a01b039092168252519081900360200190f35b610437610cdd565b610477610ce3565b6104776004803603602081101561058a57600080fd5b50351515610cf8565b610437610ff1565b610437610ff7565b610437610ffd565b6103ed600480360360608110156105c157600080fd5b50803590602081013590604001351515611003565b610477600480360360208110156105ec57600080fd5b50356001600160a01b03166111fe565b6104376004803603602081101561061257600080fd5b50356001600160a01b0316611217565b6104376004803603604081101561063857600080fd5b508035906020013561122a565b6104376004803603606081101561065b57600080fd5b5080359060208101359060400135611238565b6103ed6004803603604081101561068457600080fd5b506001600160a01b03813516906020013561136f565b610437600480360360208110156106b057600080fd5b50356001600160a01b03166115fb565b6103ed611616565b610548611677565b610477600480360360408110156106e657600080fd5b506001600160a01b038135169060200135611686565b6103ed6004803603602081101561071257600080fd5b503515156116aa565b6103ed6004803603602081101561073157600080fd5b50356116ce565b6105486116dc565b6104376116eb565b6104376116f1565b6103ed6116f7565b610548611701565b61041b611715565b61047761172b565b610437611738565b6103ed6004803603604081101561078e57600080fd5b506001600160a01b03813516906020013561173e565b61041b600480360360408110156107ba57600080fd5b506001600160a01b03813516906020013561176c565b6103ed6117a8565b61041b600480360360408110156107ee57600080fd5b506001600160a01b0381351690602001356117c1565b6103ed6004803603606081101561081a57600080fd5b506001600160a01b038135811691602081013590911690604001356117cf565b6103ed6004803603606081101561085057600080fd5b508035906001600160a01b03602082013581169160400135166118f6565b61041b6004803603604081101561088457600080fd5b506001600160a01b0381351690602001356119be565b6105486119cc565b6103ed600480360360408110156108b857600080fd5b50803590602001356119db565b6104376119e7565b6103ed611a2e565b6103ed600480360360408110156108eb57600080fd5b5080359060200135611a38565b61041b611a44565b6104376004803603604081101561091657600080fd5b506001600160a01b0381358116916020013516611a4d565b61041b611a78565b6103ed611a81565b6104376004803603604081101561095457600080fd5b5080359060200135611a8b565b6104376004803603602081101561097757600080fd5b50356001600160a01b0316611a99565b6103ed6004803603604081101561099d57600080fd5b5080359060200135611aab565b6103ed600480360360208110156109c057600080fd5b5035611ab7565b610437600480360360c08110156109dd57600080fd5b5080359060208101359060408101359060ff6060820135169060808101359060a00135611ac2565b6103ed60048036036020811015610a1b57600080fd5b50356001600160a01b0316611cab565b6103ed611cc5565b610a3f82826000611cde565b5050565b6000610a50338484611e8e565b5060015b92915050565b6002545b90565b6005546006546040805163072c21ef60e31b8152600481019290925251600092610ae8926001600160a01b03909116916339610f7891602480820192602092909190829003018186803b158015610ab757600080fd5b505afa158015610acb573d6000803e3d6000fd5b505050506040513d6020811015610ae157600080fd5b5051611ec6565b905090565b6000806000610afb846115fb565b90506000610b07610c11565b905081610b1b57600093509150610b489050565b610b42610b26611f1d565b610b36838563ffffffff611f4416565b9063ffffffff611f6b16565b93509150505b915091565b610b576001611f8d565b565b60095481565b6000610b6c84848461205a565b6001600160a01b038416600090815260016020908152604080832033808552925290912054610bac918691610ba7908663ffffffff61212516565b611e8e565b5060015b9392505050565b60146020526000908152604090205481565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610a50918590610ba7908663ffffffff61213a16565b60075481565b600d5481565b600080610c1c610a5a565b90506000610c28611f1d565b90508115610cc55760055460065460408051637f4b432360e01b8152600481019290925251610cc0928592610b369286926001600160a01b031691637f4b4323916024808301926020929190829003018186803b158015610c8857600080fd5b505afa158015610c9c573d6000803e3d6000fd5b505050506040513d6020811015610cb257600080fd5b50519063ffffffff611f4416565b610cc7565b805b9250505090565b6004546001600160a01b031681565b600c5481565b600080610cf06000610cf8565b915091509091565b6000803381610d068261214c565b90506000600a54821015610d61576040805162461bcd60e51b815260206004820152601c60248201527f546f6f20736d616c6c207265776172647320746f2072657374616b6500000000604482015290519081900360640190fd5b8115610b4257610d73826000856121fa565b905080821115610f615785610e14576005546006546040805163bc8756a960e01b8152600481019290925283850360248301526001600160a01b038681166044840152905192169163bc8756a9916064808201926020929091908290030181600087803b158015610de357600080fd5b505af1158015610df7573d6000803e3d6000fd5b505050506040513d6020811015610e0d57600080fd5b5051610ea2565b6005546006546040805163c7f067cb60e01b8152600481019290925283850360248301526001600160a01b038681166044840152905192169163c7f067cb916064808201926020929091908290030181600087803b158015610e7557600080fd5b505af1158015610e89573d6000803e3d6000fd5b505050506040513d6020811015610e9f57600080fd5b50515b610ee9576040805162461bcd60e51b8152602060048201526013602482015272496e737566666963656e74207265776172647360681b604482015290519081900360640190fd5b600480546006546040805163b7721d2d60e01b8152938401919091526001600160a01b0386811660248501528486036044850152905191169163b7721d2d91606480830192600092919082900301818387803b158015610f4857600080fd5b505af1158015610f5c573d6000803e3d6000fd5b505050505b6000610f6c84610aed565b506004805460065460408051637f88a95760e01b8152938401919091526001600160a01b0388811660248501526044840185905290519394501691637f88a9579160648082019260009290919082900301818387803b158015610fce57600080fd5b505af1158015610fe2573d6000803e3d6000fd5b50505050509350915050915091565b600f5481565b60065481565b600b5481565b6000806110118585856124b9565b336000908152601460205260408120549294509092509061103990600163ffffffff61213a16565b9050611043613447565b6040518060400160405280848152602001600560009054906101000a90046001600160a01b03166001600160a01b031663900cf0cf6040518163ffffffff1660e01b815260040160206040518083038186803b1580156110a257600080fd5b505afa1580156110b6573d6000803e3d6000fd5b505050506040513d60208110156110cc57600080fd5b50519052336000818152601560209081526040808320878452825280832085518155858301516001909101559282526014905220839055905061110d612685565b60065460408051632a9b5b4160e11b81526004810192909252336024830152604482018a90526064820187905260848201859052516001600160a01b039290921691635536b6829160a48082019260009290919082900301818387803b15801561117657600080fd5b505af115801561118a573d6000803e3d6000fd5b5050600480546006546040805163532e19a960e01b815293840191909152516001600160a01b03909116935063532e19a99250602480830192600092919082900301818387803b1580156111dd57600080fd5b505af11580156111f1573d6000803e3d6000fd5b5050505050505050505050565b6012602052600090815260409020805460019091015482565b6000610a5482611225610a61565b6127c2565b6000610bb083836000612843565b6000611242611715565b61124b57600080fd5b600f546000611260858363ffffffff61213a16565b90508061127257600092505050610bb0565b6000611297611287888463ffffffff61213a16565b610b36848863ffffffff611f4416565b905060006112af83610b36868563ffffffff611f4416565b905060006112c3838363ffffffff61212516565b6005546006546040805163858a7c0360e01b8152600481019290925260248201849052519293506001600160a01b039091169163858a7c039160448082019260009290919082900301818387803b15801561131d57600080fd5b505af1158015611331573d6000803e3d6000fd5b5050600d54611349925090508263ffffffff61212516565b600d55600f5461135f908363ffffffff61212516565b600f555090979650505050505050565b611377611715565b61138057600080fd5b61138b8260016129bd565b5060008061139884610aed565b91509150828210156113e6576040805162461bcd60e51b815260206004820152601260248201527109ad2cee4c2e8d2dcce40e8dede40daeac6d60731b604482015290519081900360640190fd5b60006113f0611f1d565b9050600061140883610b36878563ffffffff611f4416565b90506114148682612bb9565b60055460065460408051629ff11560e81b815260048101929092526000888103602484015290516001600160a01b0390931692639ff1150092604480820193929182900301818387803b15801561146a57600080fd5b505af115801561147e573d6000803e3d6000fd5b5050600d54611496925090508663ffffffff61212516565b600d55600480546006546040805163f1382b5360e01b8152938401919091526001600160a01b0389811660248501526044840189905260648401859052905191169163f1382b5391608480830192600092919082900301818387803b1580156114fe57600080fd5b505af1158015611512573d6000803e3d6000fd5b5050600480546006546040805163532e19a960e01b815293840191909152516001600160a01b03909116935063532e19a99250602480830192600092919082900301818387803b15801561156557600080fd5b505af1158015611579573d6000803e3d6000fd5b5050600480546006546040805163302df4df60e11b8152938401919091526001600160a01b038b81166024850152604484018b905290519116935063605be9be9250606480830192600092919082900301818387803b1580156115db57600080fd5b505af11580156115ef573d6000803e3d6000fd5b50505050505050505050565b6001600160a01b031660009081526020819052604090205490565b61161e611715565b61162757600080fd5b60035460405160009161010090046001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a360038054610100600160a81b0319169055565b6005546001600160a01b031681565b60156020908152600092835260408084209091529082529020805460019091015482565b6116b2611715565b6116bb57600080fd5b600e805460ff1916911515919091179055565b6116d9816001612c60565b50565b6016546001600160a01b031681565b60085481565b60105481565b610b576000611f8d565b60035461010090046001600160a01b031690565b60035461010090046001600160a01b0316331490565b600080610cf06001610cf8565b600a5481565b611746611715565b61174f57600080fd5b61175a8260016129bd565b50611767816000846121fa565b505050565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610a50918590610ba7908663ffffffff61212516565b6117b0611715565b6117b957600080fd5b610b57612d50565b6000610a5083836000612d5c565b6117d7611715565b6117e057600080fd5b6001600160a01b03831661182a576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015611824573d6000803e3d6000fd5b50611767565b826001600160a01b031663a9059cbb83836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561188a57600080fd5b505af115801561189e573d6000803e3d6000fd5b505050506040513d60208110156118b457600080fd5b5051611767576040805162461bcd60e51b815260206004820152600c60248201526b111c985a5b8819985a5b195960a21b604482015290519081900360640190fd5b600354600160a81b900460ff1615611946576040805162461bcd60e51b815260206004820152600e60248201526d185b1c9958591e481a5b9a5d195960921b604482015290519081900360640190fd5b6003805460ff60a81b1916600160a81b1790556006839055600480546001600160a01b038085166001600160a01b031992831617909255600580549284169290911691909117905561199781612dea565b61199f612685565b5050670de0b6b3a7640000600a555050600e805460ff19166001179055565b6000610a5083836001612d5c565b6017546001600160a01b031681565b610a3f82826001611cde565b6000806119f2611f1d565b905060086006541015611a06579050610a5e565b6010548015611a2857610cc081610b3684600f54611f4490919063ffffffff16565b50919050565b610b576000612e64565b610a3f82826000611003565b60035460ff1681565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600e5460ff1681565b610b576001612e64565b6000610bb083836001612843565b60136020526000908152604090205481565b610a3f82826001611003565b6116d9816000612c60565b600080611acd612ec9565b60408051623f675f60e91b815233600482015290519192506000916001600160a01b03841691637ecebe00916024808301926020929190829003018186803b158015611b1857600080fd5b505afa158015611b2c573d6000803e3d6000fd5b505050506040513d6020811015611b4257600080fd5b50516005546040805163d505accf60e01b81523360048201526001600160a01b039283166024820152604481018d9052606481018b905260ff8a16608482015260a4810189905260c4810188905290519293509084169163d505accf9160e48082019260009290919082900301818387803b158015611bc057600080fd5b505af1158015611bd4573d6000803e3d6000fd5b505060408051623f675f60e91b815233600482015290516001850193506001600160a01b0386169250637ecebe0091602480820192602092909190829003018186803b158015611c2357600080fd5b505afa158015611c37573d6000803e3d6000fd5b505050506040513d6020811015611c4d57600080fd5b505114611c92576040805162461bcd60e51b815260206004820152600e60248201526d125b9d985b1a59081c195c9b5a5d60921b604482015290519081900360640190fd5b611c9e89896001612843565b9998505050505050505050565b611cb3611715565b611cbc57600080fd5b6116d981612dea565b611ccd611715565b611cd657600080fd5b610b57613043565b600080611cec8585856124b9565b91509150611cf8613447565b50336000908152601260209081526040918290208251808401909352805480845260019091015491830191909152611d36908363ffffffff61213a16565b81526005546040805163900cf0cf60e01b815290516001600160a01b039092169163900cf0cf91600480820192602092909190829003018186803b158015611d7d57600080fd5b505afa158015611d91573d6000803e3d6000fd5b505050506040513d6020811015611da757600080fd5b505160208281019182523360008181526012909252604080832085518155935160019094019390935560048054600654855163f1382b5360e01b8152928301526024820192909252604481018a90526064810187905292516001600160a01b0390911692839263f1382b539260848084019382900301818387803b158015611e2e57600080fd5b505af1158015611e42573d6000803e3d6000fd5b50505050806001600160a01b031663532e19a96006546040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156111dd57600080fd5b6040805162461bcd60e51b8152602060048201526008602482015267191a5cd8589b195960c21b604482015290519081900360640190fd5b600c546000908215610a54576000611edc610a5a565b90508015611f1657611f13611f0682610b36876a084595161401484a00000063ffffffff611f4416565b839063ffffffff61213a16565b91505b5092915050565b600060086006541015611f3257506064610a5e565b506c01431e0fae6d7217caa000000090565b600082611f5357506000610a54565b82820282848281611f6057fe5b0414610bb057600080fd5b6000808211611f7957600080fd5b6000828481611f8457fe5b04949350505050565b611f95613447565b50336000908152601260209081526040808320815180830190925280548252600101549181019190915290611fca8284613052565b3360008181526012602052604080822082815560010182905560048054600654835163302df4df60e11b81529283015260248201949094526044810185905290519394506001600160a01b039092169263605be9be92606480820193929182900301818387803b15801561203d57600080fd5b505af1158015612051573d6000803e3d6000fd5b50505050505050565b6001600160a01b03821661206d57600080fd5b6001600160a01b038316600090815260208190526040902054612096908263ffffffff61212516565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546120cb908263ffffffff61213a16565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60008282111561213457600080fd5b50900390565b600082820183811015610bb057600080fd5b6000806121c3600560009054906101000a90046001600160a01b03166001600160a01b0316637ed4b27c6006546040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156121af57600080fd5b505af1158015610acb573d6000803e3d6000fd5b905060006121d184836127c2565b600c8390556001600160a01b038516600090815260136020526040902092909255509050919050565b600061220461335e565b600e5460ff16612254576040805162461bcd60e51b815260206004820152601660248201527511195b1959d85d1a5bdb881a5cc8191a5cd8589b195960521b604482015290519081900360640190fd5b600061225e610c11565b9050600061226a611f1d565b9050600061228283610b36898563ffffffff611f4416565b9050858110156122cd576040805162461bcd60e51b8152602060048201526011602482015270546f6f206d75636820736c69707061676560781b604482015290519081900360640190fd5b6001600160a01b03851660009081526012602052604090205415612327576040805162461bcd60e51b815260206004820152600c60248201526b13db99dbda5b99c8195e1a5d60a21b604482015290519081900360640190fd5b612331858261339f565b61234582610b36858463ffffffff611f4416565b60055460065460408051629ff11560e81b8152600481019290925260248201849052519299506001600160a01b0390911691639ff115009160448082019260009290919082900301818387803b15801561239e57600080fd5b505af11580156123b2573d6000803e3d6000fd5b5050600d546123ca925090508863ffffffff61213a16565b600d55600480546006546040805163c69d057360e01b8152938401919091526001600160a01b038881166024850152604484018b9052606484018590529051911691829163c69d05739160848082019260009290919082900301818387803b15801561243557600080fd5b505af1158015612449573d6000803e3d6000fd5b50505050806001600160a01b031663532e19a96006546040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561249557600080fd5b505af11580156124a9573d6000803e3d6000fd5b50999a9950505050505050505050565b6000806000806124c833610aed565b91509150816000141580156124dd5750868210155b612523576040805162461bcd60e51b8152602060048201526012602482015271151bdbc81b5d58da081c995c5d595cdd195960721b604482015290519081900360640190fd5b600061252d611f1d565b9050600061254583610b368b8563ffffffff611f4416565b905087811115612590576040805162461bcd60e51b8152602060048201526011602482015270746f6f206d75636820736c69707061676560781b604482015290519081900360640190fd5b61259a33886129bd565b506125a53382612bb9565b60055460065460408051629ff11560e81b8152600481019290925260008c8103602484015290516001600160a01b0390931692639ff1150092604480820193929182900301818387803b1580156125fb57600080fd5b505af115801561260f573d6000803e3d6000fd5b5050600d54612627925090508a63ffffffff61212516565b600d5560006126476126376119e7565b610b368c8663ffffffff611f4416565b600f5490915061265d908b63ffffffff61213a16565b600f55601054612673908263ffffffff61213a16565b60105590999098509650505050505050565b6016546000906001600160a01b031680610ae857600560009054906101000a90046001600160a01b03166001600160a01b0316635ab1bd536040518163ffffffff1660e01b815260040160206040518083038186803b1580156126e757600080fd5b505afa1580156126fb573d6000803e3d6000fd5b505050506040513d602081101561271157600080fd5b5051604080516832bb32b73a39a43ab160b91b8152815190819003600901812063cac39a0560e01b8252600482015290516001600160a01b039092169163cac39a0591602480820192602092909190829003018186803b15801561277457600080fd5b505afa158015612788573d6000803e3d6000fd5b505050506040513d602081101561279e57600080fd5b5051601680546001600160a01b0319166001600160a01b0383161790559050905090565b6000806127ce846115fb565b9050806127df576000915050610a54565b6001600160a01b0384166000908152601360205260409020548381141561280b57600092505050610a54565b61283a6a084595161401484a000000610b368461282e888663ffffffff61212516565b9063ffffffff611f4416565b95945050505050565b600061284f33836129bd565b5061285b8484336121fa565b9050816128f15760055460065460408051636901b25360e01b8152600481019290925260248201849052336044830152516001600160a01b0390921691636901b253916064808201926020929091908290030181600087803b1580156128c057600080fd5b505af11580156128d4573d6000803e3d6000fd5b505050506040513d60208110156128ea57600080fd5b505161297b565b60055460065460408051624c025560e81b8152600481019290925260248201849052336044830152516001600160a01b0390921691634c025500916064808201926020929091908290030181600087803b15801561294e57600080fd5b505af1158015612962573d6000803e3d6000fd5b505050506040513d602081101561297857600080fd5b50515b610bb0576040805162461bcd60e51b815260206004820152600e60248201526d19195c1bdcda5d0819985a5b195960921b604482015290519081900360640190fd5b6000806129c98461214c565b90508015610bb05782612a67576005546006546040805163bc8756a960e01b81526004810192909252602482018490526001600160a01b038781166044840152905192169163bc8756a9916064808201926020929091908290030181600087803b158015612a3657600080fd5b505af1158015612a4a573d6000803e3d6000fd5b505050506040513d6020811015612a6057600080fd5b5051612af4565b6005546006546040805163c7f067cb60e01b81526004810192909252602482018490526001600160a01b038781166044840152905192169163c7f067cb916064808201926020929091908290030181600087803b158015612ac757600080fd5b505af1158015612adb573d6000803e3d6000fd5b505050506040513d6020811015612af157600080fd5b50515b612b3b576040805162461bcd60e51b8152602060048201526013602482015272496e737566666963656e74207265776172647360681b604482015290519081900360640190fd5b600480546006546040805163b7721d2d60e01b8152938401919091526001600160a01b03878116602485015260448401859052905191169163b7721d2d91606480830192600092919082900301818387803b158015612b9957600080fd5b505af1158015612bad573d6000803e3d6000fd5b50505050905092915050565b6001600160a01b038216612bcc57600080fd5b600254612bdf908263ffffffff61212516565b6002556001600160a01b038216600090815260208190526040902054612c0b908263ffffffff61212516565b6001600160a01b038316600081815260208181526040808320949094558351858152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35050565b612c68613447565b503360009081526015602090815260408083208584528252808320815180830190925280548252600101549181019190915290612ca58284613052565b3360009081526015602090815260408083208884529091528120818155600101559050612cd0612685565b60065460408051636e699d8760e01b815260048101929092523360248301526044820184905260648201879052516001600160a01b039290921691636e699d879160848082019260009290919082900301818387803b158015612d3257600080fd5b505af1158015612d46573d6000803e3d6000fd5b5050505050505050565b6003805460ff19169055565b33612d6784836129bd565b50612d7281836129bd565b50612d7e81858561205a565b612d86612685565b6006546040805163c08b3f9d60e01b815260048101929092526001600160a01b038481166024840152878116604484015260648301879052905192169163c08b3f9d9160848082019260009290919082900301818387803b158015612d3257600080fd5b6001600160a01b038116612dfd57600080fd5b6003546040516001600160a01b0380841692610100900416907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600380546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b6000612e7033836129bd565b9050600a54811015610a3f576040805162461bcd60e51b815260206004820152601860248201527f546f6f20736d616c6c207265776172647320616d6f756e740000000000000000604482015290519081900360640190fd5b6017546000906001600160a01b031680610ae857600560009054906101000a90046001600160a01b03166001600160a01b0316635ab1bd536040518163ffffffff1660e01b815260040160206040518083038186803b158015612f2b57600080fd5b505afa158015612f3f573d6000803e3d6000fd5b505050506040513d6020811015612f5557600080fd5b505160408051621c1bdb60ea1b8152815190819003600301812063cac39a0560e01b8252600482015290516001600160a01b039092169163cac39a0591602480820192602092909190829003018186803b158015612fb257600080fd5b505afa158015612fc6573d6000803e3d6000fd5b505050506040513d6020811015612fdc57600080fd5b505190506001600160a01b038116613023576040805162461bcd60e51b81526020600482015260056024820152641d5b9cd95d60da1b604482015290519081900360640190fd5b601780546001600160a01b0319166001600160a01b038316179055919050565b6003805460ff19166001179055565b81516005546040805163900cf0cf60e01b81529051600093926001600160a01b03169163900cf0cf916004808301926020929190829003018186803b15801561309a57600080fd5b505afa1580156130ae573d6000803e3d6000fd5b505050506040513d60208110156130c457600080fd5b50516005546040805163a7ab696160e01b81529051613149926001600160a01b03169163a7ab6961916004808301926020929190829003018186803b15801561310c57600080fd5b505afa158015613120573d6000803e3d6000fd5b505050506040513d602081101561313657600080fd5b505160208701519063ffffffff61213a16565b111580156131575750600081115b6131a8576040805162461bcd60e51b815260206004820152601c60248201527f496e636f6d706c657465207769746864726177616c20706572696f6400000000604482015290519081900360640190fd5b60006131c16131b5611f1d565b610b368461282e6119e7565b6010549091506131d7908363ffffffff61212516565b601055600f546131ed908263ffffffff61212516565b600f5583613284576005546006546040805163bc8756a960e01b8152600481019290925260248201849052336044830152516001600160a01b039092169163bc8756a9916064808201926020929091908290030181600087803b15801561325357600080fd5b505af1158015613267573d6000803e3d6000fd5b505050506040513d602081101561327d57600080fd5b505161330f565b6005546006546040805163c7f067cb60e01b8152600481019290925260248201849052336044830152516001600160a01b039092169163c7f067cb916064808201926020929091908290030181600087803b1580156132e257600080fd5b505af11580156132f6573d6000803e3d6000fd5b505050506040513d602081101561330c57600080fd5b50515b613356576040805162461bcd60e51b8152602060048201526013602482015272496e737566666963656e74207265776172647360681b604482015290519081900360640190fd5b949350505050565b60035460ff1615610b57576040805162461bcd60e51b81526020600482015260066024820152651b1bd8dad95960d21b604482015290519081900360640190fd5b6001600160a01b0382166133b257600080fd5b6002546133c5908263ffffffff61213a16565b6002556001600160a01b0382166000908152602081905260409020546133f1908263ffffffff61213a16565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b60405180604001604052806000815260200160008152509056fea265627a7a723158204218c2c8b2e87f177283c78647790865bea3a76c3c8e35d83898fc32476d052564736f6c63430005110032", + "nonce": "0x8d", + "chainId": "0xaa36a7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0xcf4a40", + "logs": [ + { + "address": "0x777badc2c75bd4ee5303bf6e90009bd31d8fd3d1", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000724d72eb61e508d81ca701881f2248f092953bf" + ], + "data": "0x", + "blockHash": "0x80bb5b7121caad8c640dafc4e50623df1bc4757fbe8f5cd035bd702d975119ec", + "blockNumber": "0x605f17", + "transactionHash": "0x4499d0ecf70c3a39927d5245cac653c02f0f0c33dc82be6a9ce5d37d4615c70a", + "transactionIndex": "0x40", + "logIndex": "0x73", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000002000001000000000000004000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000020000010000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x4499d0ecf70c3a39927d5245cac653c02f0f0c33dc82be6a9ce5d37d4615c70a", + "transactionIndex": "0x40", + "blockHash": "0x80bb5b7121caad8c640dafc4e50623df1bc4757fbe8f5cd035bd702d975119ec", + "blockNumber": "0x605f17", + "gasUsed": "0x526898", + "effectiveGasPrice": "0x3be653915", + "from": "0x0724d72eb61e508d81ca701881f2248f092953bf", + "to": null, + "contractAddress": "0x777badc2c75bd4ee5303bf6e90009bd31d8fd3d1" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xfcdd6b", + "logs": [ + { + "address": "0x19b0b3256b22116096a2ef8cafc6e832ba18a947", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000724d72eb61e508d81ca701881f2248f092953bf" + ], + "data": "0x", + "blockHash": "0x80bb5b7121caad8c640dafc4e50623df1bc4757fbe8f5cd035bd702d975119ec", + "blockNumber": "0x605f17", + "transactionHash": "0xae07c932b4a5459bd683767dde161e9fa7ad1cc9b0ac83a39f9c291bcb22b79f", + "transactionIndex": "0x41", + "logIndex": "0x74", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000100100400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000020000010000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xae07c932b4a5459bd683767dde161e9fa7ad1cc9b0ac83a39f9c291bcb22b79f", + "transactionIndex": "0x41", + "blockHash": "0x80bb5b7121caad8c640dafc4e50623df1bc4757fbe8f5cd035bd702d975119ec", + "blockNumber": "0x605f17", + "gasUsed": "0x2d932b", + "effectiveGasPrice": "0x3be653915", + "from": "0x0724d72eb61e508d81ca701881f2248f092953bf", + "to": null, + "contractAddress": "0x19b0b3256b22116096a2ef8cafc6e832ba18a947" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1721057742, + "chain": 11155111, + "commit": "a5fd7d69" +} \ No newline at end of file diff --git a/broadcast/UpgradeStakeManager_Sepolia.s.sol/11155111/run-latest.json b/broadcast/UpgradeStakeManager_Sepolia.s.sol/11155111/run-latest.json new file mode 100644 index 00000000..eceb3423 --- /dev/null +++ b/broadcast/UpgradeStakeManager_Sepolia.s.sol/11155111/run-latest.json @@ -0,0 +1,112 @@ +{ + "transactions": [ + { + "hash": "0x4499d0ecf70c3a39927d5245cac653c02f0f0c33dc82be6a9ce5d37d4615c70a", + "transactionType": "CREATE", + "contractName": null, + "contractAddress": "0x777badc2c75bd4ee5303bf6e90009bd31d8fd3d1", + "function": null, + "arguments": null, + "transaction": { + "from": "0x0724d72eb61e508d81ca701881f2248f092953bf", + "gas": "0x6b19b6", + "value": "0x0", + "input": "0x60806040526023805460ff191690553480156200001b57600080fd5b5060008054610100600160a81b0319168155600180546001600160a01b0319163317908190556040516001600160a01b039190911691907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a360235460ff1615620000c1576040805162461bcd60e51b815260206004820152600e60248201526d185b1c9958591e481a5b9a5d195960921b604482015290519081900360640190fd5b6023805460ff19166001179055615fc580620000de6000396000f3fe608060405234801561001057600080fd5b50600436106107175760003560e01c80637d669752116103a8578063b1d23f02116101f4578063e8afa8e81161011a578063f5e95acb116100b8578063fb1ef52c11610087578063fb1ef52c14611826578063fba58f341461184f578063fc0c546a14611857578063ff3db8e31461185f57610717565b8063f5e95acb1461178f578063f771fc87146117f9578063f83d08ba14611816578063f8a3176c1461181e57610717565b8063f24ccbfe116100f4578063f24ccbfe14611697578063f28699fa1461169f578063f2fde38b146116be578063f41a9642146116e457610717565b8063e8afa8e81461164c578063eceec1d314611672578063f1674fdf1461168f57610717565b8063cf30901211610192578063dcd962b211610161578063dcd962b2146115fc578063e568959a1461161f578063e59ee0c614611627578063e6692f491461162f57610717565b8063cf3090121461159b578063d6de07d0146115a3578063d7f5549d146115c2578063d86d53e7146115df57610717565b8063c710e922116101ce578063c710e92214611518578063c7f067cb14611544578063cbf383d514611576578063cd6b83881461159357610717565b8063b1d23f02146114a6578063b65de35e146114c9578063bc8756a9146114e657610717565b80639342c8f4116102d95780639ff1150011610277578063a69df4b511610246578063a69df4b514611462578063a7ab69611461146a578063a8dc889b14611472578063b184be811461147a57610717565b80639ff1150014611397578063a440ab1e146113ba578063a4769071146113d7578063a6854877146113df57610717565b806399d18f6f116102b357806399d18f6f1461132c5780639a8a62431461134f5780639b33f434146113575780639ddbbf851461137457610717565b80639342c8f4146112dc57806396a93224146112f9578063987ab9db1461132457610717565b806386a22666116103465780638f32d59b116103205780638f32d59b146112a7578063900cf0cf146112af57806391460149146112b757806391f1a3a5146112d457610717565b806386a226661461127a578063883b455f146112975780638da5cb5b1461129f57610717565b80637f952d95116103825780637f952d951461118057806381254e1214611188578063817b1cd21461124f578063858a7c031461125757610717565b80637d6697521461113e5780637ed4b27c146111465780637f4b43231461116357610717565b806341b3d185116105675780635ab1bd53116104985780637060054d116104365780637854f354116104055780637854f354146110eb57806378f84a44146111085780637b1039991461112e5780637c7eaf1a1461113657610717565b80637060054d146110cb578063715018a6146110d357806376671808146110db57806377939d10146110e357610717565b80636352211e116104725780636352211e14610fa05780636365679814610fbd57806368cb812a14610fe95780636901b2531461109957610717565b80635ab1bd5314610f015780635e47655f14610f0957806360c8d12214610f7757610717565b80634fdd20f11161050557806354b8c601116104df57806354b8c60114610ecc5780635508d8e114610ed457806356342d8c14610edc5780635aa6e67514610ef957610717565b80634fdd20f114610ca657806352b8115d14610d6d5780635325e14414610e0557610717565b80634b341aed116105415780634b341aed14610c3e5780634c02550014610c645780634e3c83f114610c965780634fd101d714610c9e57610717565b806341b3d18514610bf2578063451b598514610bfa57806348ab8b2a14610c0257610717565b80632079fb9a1161064c5780632fa9d18b116105ea57806335aa2e44116105b957806335aa2e4414610ac85780633862da0b14610b8157806339610f7814610ba75780633c34355614610bc457610717565b80632fa9d18b14610a0c57806330cbe57114610a9b57806331c2273b14610ab85780633427458614610ac057610717565b80632649263a116106265780632649263a146108f257806328cc4e41146109235780632cf44a431461094e5780632e17de78146109ef57610717565b80632079fb9a1461089757806325316411146108b457806325726df2146108bc57610717565b8063174e6832116106b95780631ae4818f116106935780631ae4818f1461082f5780631c404cec146108375780631dd6b9b1146108545780631e9b12ef1461087157610717565b8063174e6832146107e4578063178c2c831461080a57806317c2b9101461082757610717565b80630e15561a116106f55780630e15561a146107935780630ebb172a1461079b57806316827b1b146107a35780631705a3bd146107c057610717565b806306cfb1041461071c578063078a13b11461073b5780630cccfc5814610779575b600080fd5b6107396004803603602081101561073257600080fd5b503561187c565b005b6107396004803603608081101561075157600080fd5b506001600160a01b038135811691602081013582169160408201358116916060013516611889565b610781611901565b60408051918252519081900360200190f35b610781611907565b61078161190d565b610739600480360360208110156107b957600080fd5b5035611913565b6107c861199c565b604080516001600160a01b039092168252519081900360200190f35b610781600480360360208110156107fa57600080fd5b50356001600160a01b03166119ab565b6107396004803603602081101561082057600080fd5b5035611a35565b610781611cc2565b6107c8611cc8565b6107396004803603602081101561084d57600080fd5b5035611cd7565b6107396004803603602081101561086a57600080fd5b5035611cf0565b6107396004803603602081101561088757600080fd5b50356001600160a01b0316611cfd565b6107c8600480360360208110156108ad57600080fd5b5035611d3a565b610781611d61565b6108d9600480360360208110156108d257600080fd5b5035611d67565b6040805192835260208301919091528051918290030190f35b61090f6004803603602081101561090857600080fd5b5035611d80565b604080519115158252519081900360200190f35b6107396004803603606081101561093957600080fd5b50803590602081013590604001351515611dbb565b6107396004803603602081101561096457600080fd5b810190602081018135600160201b81111561097e57600080fd5b82018360208201111561099057600080fd5b803590602001918460208302840111600160201b831117156109b157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611de0945050505050565b61073960048036036020811015610a0557600080fd5b5035611e04565b610781600480360360a0811015610a2257600080fd5b8135916020810135916040820135916001600160a01b036060820135169181019060a081016080820135600160201b811115610a5d57600080fd5b820183602082011115610a6f57600080fd5b803590602001918460608302840111600160201b83111715610a9057600080fd5b509092509050611e19565b61073960048036036020811015610ab157600080fd5b503561212a565b6107c861213f565b61078161214e565b610ae560048036036020811015610ade57600080fd5b5035612154565b604051808e81526020018d81526020018c81526020018b81526020018a8152602001896001600160a01b03166001600160a01b03168152602001886001600160a01b03166001600160a01b03168152602001876003811115610b4357fe5b60ff1681526020018681526020018581526020018481526020018381526020018281526020019d505050505050505050505050505060405180910390f35b61078160048036036020811015610b9757600080fd5b50356001600160a01b03166121c5565b61078160048036036020811015610bbd57600080fd5b50356121d7565b61073960048036036040811015610bda57600080fd5b506001600160a01b0381358116916020013516612238565b6107816122f1565b6107816122f7565b61073960048036036080811015610c1857600080fd5b508035906001600160a01b036020820135811691604081013590911690606001356122fd565b61078160048036036020811015610c5457600080fd5b50356001600160a01b03166123a3565b61090f60048036036060811015610c7a57600080fd5b50803590602081013590604001356001600160a01b03166124da565b6107816124fb565b610781612501565b610739600480360360a0811015610cbc57600080fd5b6001600160a01b0382351691602081013591604082013591606081013515159181019060a081016080820135600160201b811115610cf957600080fd5b820183602082011115610d0b57600080fd5b803590602001918460018302840111600160201b83111715610d2c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550612507945050505050565b610739600480360360c0811015610d8357600080fd5b6001600160a01b038235169160208101359160408201359160608101359160808201351515919081019060c0810160a0820135600160201b811115610dc757600080fd5b820183602082011115610dd957600080fd5b803590602001918460018302840111600160201b83111715610dfa57600080fd5b509092509050612525565b610e2260048036036020811015610e1b57600080fd5b5035612648565b60405180868152602001858152602001846001600160a01b03166001600160a01b031681526020018315151515815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610e8d578181015183820152602001610e75565b50505050905090810190601f168015610eba5780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390f35b61090f61270e565b610781612717565b6107c860048036036020811015610ef257600080fd5b503561271d565b6107c861273b565b6107c861274f565b61078160048036036020811015610f1f57600080fd5b810190602081018135600160201b811115610f3957600080fd5b820183602082011115610f4b57600080fd5b803590602001918460018302840111600160201b83111715610f6c57600080fd5b50909250905061275f565b61073960048036036060811015610f8d57600080fd5b5080359060208101359060400135612766565b6107c860048036036020811015610fb657600080fd5b50356127cf565b61073960048036036040811015610fd357600080fd5b506001600160a01b03813516906020013561281b565b61073960048036036060811015610fff57600080fd5b813591602081013591810190606081016040820135600160201b81111561102557600080fd5b82018360208201111561103757600080fd5b803590602001918460018302840111600160201b8311171561105857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550612832945050505050565b61090f600480360360608110156110af57600080fd5b50803590602081013590604001356001600160a01b0316612978565b610781612990565b610739612996565b6107816129f1565b6107816129f7565b6107396004803603602081101561110157600080fd5b50356129fd565b6107816004803603602081101561111e57600080fd5b50356001600160a01b0316612a16565b6107c8612a28565b610781612a37565b610781612a3d565b6107816004803603602081101561115c57600080fd5b5035612a43565b6107816004803603602081101561117957600080fd5b5035612a9a565b610781612aaf565b610739600480360360a081101561119e57600080fd5b6001600160a01b0382351691602081013591604082013591606081013515159181019060a081016080820135600160201b8111156111db57600080fd5b8201836020820111156111ed57600080fd5b803590602001918460018302840111600160201b8311171561120e57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550612ab5945050505050565b610781612acc565b6107396004803603604081101561126d57600080fd5b5080359060200135612ad2565b6107396004803603602081101561129057600080fd5b5035612b18565b6107c8612b29565b6107c8612b3d565b61090f612b4c565b610781612b8f565b610739600480360360208110156112cd57600080fd5b5035612b95565b610781612bab565b610739600480360360208110156112f257600080fd5b5035612bb1565b6107396004803603606081101561130f57600080fd5b50803590602081013590604001351515612bc6565b6107c8612be5565b6107396004803603604081101561134257600080fd5b5080359060200135612bf4565b610781612c5a565b6107396004803603602081101561136d57600080fd5b5035612c60565b6107396004803603604081101561138a57600080fd5b5080359060200135612d1c565b610739600480360360408110156113ad57600080fd5b5080359060200135612d82565b610739600480360360208110156113d057600080fd5b5035612e75565b610781612e8c565b610739600480360360808110156113f557600080fd5b813591602081013591604082013515159190810190608081016060820135600160201b81111561142457600080fd5b82018360208201111561143657600080fd5b803590602001918460018302840111600160201b8311171561145757600080fd5b509092509050612e92565b610739612f43565b610781612f55565b610781612f5b565b6107396004803603604081101561149057600080fd5b506001600160a01b038135169060200135612f61565b610739600480360360408110156114bc57600080fd5b5080359060200135612f75565b610781600480360360208110156114df57600080fd5b5035612f88565b61090f600480360360608110156114fc57600080fd5b50803590602081013590604001356001600160a01b0316612fd6565b6107396004803603604081101561152e57600080fd5b50803590602001356001600160a01b0316612fed565b61090f6004803603606081101561155a57600080fd5b50803590602081013590604001356001600160a01b03166130a8565b6107396004803603602081101561158c57600080fd5b50356130b7565b610781613140565b61090f613146565b610739600480360360208110156115b957600080fd5b5035151561314f565b610781600480360360208110156115d857600080fd5b50356132a1565b610739600480360360208110156115f557600080fd5b50356132b3565b6107396004803603604081101561161257600080fd5b50803590602001356132c8565b610781613330565b6108d9613336565b6107396004803603602081101561164557600080fd5b503561333f565b6107396004803603602081101561166257600080fd5b50356001600160a01b03166133fb565b6107816004803603602081101561168857600080fd5b503561347b565b6107c861348d565b6107c861349c565b610739600480360360208110156116b557600080fd5b503515156134ab565b610739600480360360208110156116d457600080fd5b50356001600160a01b03166134c6565b610739600480360360408110156116fa57600080fd5b81359190810190604081016020820135600160201b81111561171b57600080fd5b82018360208201111561172d57600080fd5b803590602001918460018302840111600160201b8311171561174e57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506134e0945050505050565b61073960048036036101208110156117a657600080fd5b506001600160a01b038135811691602081013582169160408201358116916060810135821691608082013581169160a081013582169160c082013581169160e081013582169161010090910135166136e5565b6107396004803603602081101561180f57600080fd5b5035613888565b6107396138a3565b6107c86138b3565b6107396004803603606081101561183c57600080fd5b50803590602081013590604001356138c2565b6107816139dd565b6107c86139e3565b6107396004803603602081101561187557600080fd5b50356139f2565b611884613a07565b600f55565b611891613a07565b61189a81613a55565b6118a357600080fd5b60238054610100600160a81b0319169055602580546001600160a01b03199081166001600160a01b03938416179091556006805482169583169590951790945560058054851693821693909317909255600780549093169116179055565b60155481565b60135481565b60085481565b61191b613a07565b8061192557600080fd5b60055460105460408051630f1980a560e41b8152600481018590526024810192909252516001600160a01b039092169163f1980a509160448082019260009290919082900301818387803b15801561197c57600080fd5b505af1158015611990573d6000803e3d6000fd5b50505060109190915550565b602c546001600160a01b031681565b60065460408051632f745c5960e01b81526001600160a01b038481166004830152600060248301819052925192931691632f745c5991604480820192602092909190829003018186803b158015611a0157600080fd5b505afa158015611a15573d6000803e3d6000fd5b505050506040513d6020811015611a2b57600080fd5b505190505b919050565b80611a3f81613a74565b60026000838152601a6020526040902060060154600160a01b900460ff166003811115611a6857fe5b14611aa7576040805162461bcd60e51b815260206004820152600a602482015269139bdd081a985a5b195960b21b604482015290519081900360640190fd5b6000828152601a602052604090206003015415611aff576040805162461bcd60e51b8152602060048201526011602482015270416c726561647920756e7374616b696e6760781b604482015290519081900360640190fd5b6009546000838152601a6020526040902060040154811015611b61576040805162461bcd60e51b8152602060048201526016602482015275125b98dbdb5c1b195d19481a985a5b081c195c9a5bd960521b604482015290519081900360640190fd5b6000838152601a6020526040902054600c54811015611b7f57600080fd5b6000848152601a60205260409020600601546001600160a01b03168015611bf457806001600160a01b031663a69df4b56040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611bdb57600080fd5b505af1158015611bef573d6000803e3d6000fd5b505050505b6000858152601a60205260409020600a0154611c2490611c1b90849063ffffffff613afe16565b60016000613b10565b6000858152601a602052604080822060068101805460ff60a01b1916600160a01b1790556005908101549054825163f92ec5af60e01b8152600481018a90526001600160a01b039283166024820181905293519394929091169263f92ec5af9260448084019391929182900301818387803b158015611ca257600080fd5b505af1158015611cb6573d6000803e3d6000fd5b50505050505050505050565b60175481565b6007546001600160a01b031681565b80611ce181613a74565b611cec826001613bcc565b5050565b611cf8613a07565b600955565b611d05613a07565b6001600160a01b038116611d1857600080fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b60268181548110611d4757fe5b6000918252602090912001546001600160a01b0316905081565b600e5481565b601e602052600090815260409020805460019091015482565b6000818152601a6020526040812060068101548154600390920154600954611db593600160a01b90930460ff16929190613be0565b92915050565b611dc3613c17565b82611dcd81613a74565b611dda8484846000613c58565b50505050565b611de8612b4c565b611df157600080fd5b8051611cec906026906020840190615e39565b80611e0e81613a74565b611cec826000613e32565b6002546000906001600160a01b03163314611e3357600080fd5b600954601d546000908190611e46615e9e565b6026546040805191840180835260208082028401019091528015611e74578160200160208202803883390190505b5060408083019190915260268054825160208083028201810190945281815292830182828015611ecd57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611eaf575b50505050506060820152600060208201526026546080820152611eee615ecd565b6026546040805191850180835260208082028401019091528015611f1c578160200160208202803883390190505b50602082015260005b888110156120eb576000611f738e8c8c85818110611f3f57fe5b9050606002016003806020026040519081016040528092919082600360200280828437600092019190915250613ee5915050565b9050856001600160a01b0316816001600160a01b03161415611f9557506120e3565b856001600160a01b0316816001600160a01b03161015611fb557506120eb565b6001600160a01b0381166000908152601b6020908152604080832054808452601a909252909120805460068201546003909201548087529091600160a01b900460ff169061200790829084908e613be0565b15612092576000838152601a60205260409020600a0154939850889361204690839061203a908d9063ffffffff613afe16565b9063ffffffff613afe16565b8651909a50156120805782866020015187604001518151811061206557fe5b6020908102919091010152604086018051600101905261208d565b61208a8785613fe8565b96505b6120de565b60028160038111156120a057fe5b14156120de578287604001518860000151815181106120bb57fe5b602090810291909101810191909152875160019081018952908801805190910190525b505050505b600101611f25565b506120f7826000613fe8565b91506121198d8b878e86604001518760000151876020015188604001516140c5565b9d9c50505050505050505050505050565b8061213481613a74565b611cec826001613e32565b6006546001600160a01b031681565b60165481565b601a60205260009081526040902080546001820154600283015460038401546004850154600586015460068701546007880154600889015460098a0154600a8b0154600b909b0154999a98999798969795966001600160a01b039586169695851695600160a01b90950460ff16948d565b601b6020526000908152604090205481565b6000818152601a602052604081206003015481906121fc576121f88361422d565b9150505b6000838152601a602052604090206009015461223190600190612225908463ffffffff613afe16565b9063ffffffff6142a916565b9392505050565b612240613a07565b60038054602b80546001600160a01b038084166001600160a01b0319928316179283905592811686841617909355602c805490931684831617909255604080516370a0823160e01b81523060048201529051611cec93909216916370a0823191602480820192602092909190829003018186803b1580156122c057600080fd5b505afa1580156122d4573d6000803e3d6000fd5b505050506040513d60208110156122ea57600080fd5b50516142be565b600c5481565b60295481565b612305613a07565b6000848152601a60205260409020600601546001600160a01b03168061232a57600080fd5b6040805163abf59fc960e01b81526001600160a01b03868116600483015285811660248301526044820185905291519183169163abf59fc99160648082019260009290919082900301818387803b15801561238457600080fd5b505af1158015612398573d6000803e3d6000fd5b505050505050505050565b60006001600160a01b03821615806124315750600654604080516370a0823160e01b81526001600160a01b038581166004830152915191909216916370a08231916024808301926020929190829003018186803b15801561240357600080fd5b505afa158015612417573d6000803e3d6000fd5b505050506040513d602081101561242d57600080fd5b5051155b1561243e57506000611a30565b60065460408051632f745c5960e01b81526001600160a01b0385811660048301526000602483018190529251601a949190911691632f745c59916044808301926020929190829003018186803b15801561249757600080fd5b505afa1580156124ab573d6000803e3d6000fd5b505050506040513d60208110156124c157600080fd5b5051815260208101919091526040016000205492915050565b6000836124e68161445d565b6124f2848460016144cb565b95945050505050565b600f5481565b60105481565b61250f613c17565b61251e85858585856000614571565b5050505050565b333014612567576040805162461bcd60e51b815260206004820152600b60248201526a1b9bdd08185b1b1bddd95960aa1b604482015290519081900360640190fd5b61257687888860006001614623565b6125848560095460016146f0565b60006125c888868686868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061485492505050565b6005546040805163e12ab1af60e01b815260048101849052602481018a90526044810189905290519293506001600160a01b039091169163e12ab1af9160648082019260009290919082900301818387803b15801561262657600080fd5b505af115801561263a573d6000803e3d6000fd5b505050505050505050505050565b60208080526000918252604091829020805460018083015460028085015460038601805489516101009682161596909602600019011692909204601f8101889004880285018801909852878452939691956001600160a01b03851695600160a01b90950460ff16949392908301828280156127045780601f106126d957610100808354040283529160200191612704565b820191906000526020600020905b8154815290600101906020018083116126e757829003601f168201915b5050505050905085565b60195460ff1681565b60125481565b6000908152601a60205260409020600601546001600160a01b031690565b60005461010090046001600160a01b031681565b6004546001600160a01b03165b90565b6000806000fd5b61276e613a07565b60255460408051602481018690526044810185905260648082018590528251808303909101815260849091019091526020810180516001600160e01b0316633064689160e11b1790526127ca916001600160a01b031690614c61565b505050565b600654604080516331a9108f60e11b81526004810184905290516000926001600160a01b031691636352211e916024808301926020929190829003018186803b158015611a0157600080fd5b612823613c17565b611cec82338360006001614623565b6017546040805133602080830191909152818301879052825180830384018152606090920190925280519101206128719184908463ffffffff614c8916565b6128b4576040805162461bcd60e51b815260206004820152600f60248201526e2bb937b7339030b1b190383937b7b360891b604482015290519081900360640190fd5b336000908152601f60205260408120546128d590859063ffffffff6142a916565b6022549091506128eb908263ffffffff6142a916565b6022556005546040805163122b648160e01b81523360048201526024810184905290516001600160a01b039092169163122b64819160448082019260009290919082900301818387803b15801561294157600080fd5b505af1158015612955573d6000803e3d6000fd5b5050336000818152601f60205260408120889055611dda93509091508390614ddf565b6000836129848161445d565b6124f2848460006144cb565b600a5481565b61299e612b4c565b6129a757600080fd5b6001546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b60095481565b60185481565b612a05613a07565b612a138160095460016146f0565b50565b601f6020526000908152604090205481565b6004546001600160a01b031681565b602a5481565b600b5481565b600081612a4f8161445d565b612a5883614ec9565b6000838152601a6020526040812060090154612a7b90600163ffffffff6142a916565b6000858152601a60205260409020600160099091015592505050919050565b6000908152601a60205260409020600a015490565b601d5490565b612abd613c17565b61251e85858585856001614571565b60115481565b81612adc8161445d565b6000838152601a60205260409020600a0154612afe908363ffffffff6142a916565b6000938452601a6020526040909320600a01929092555050565b612b20613a07565b612a13816142be565b60235461010090046001600160a01b031681565b6001546001600160a01b031690565b604080517f6d617469632e6e6574776f726b2e70726f78792e6f776e6572000000000000008152905190819003601901902054336001600160a01b039091161490565b60095490565b612b9d613a07565b612a138160095460006146f0565b60275481565b80612bbb81613a74565b611cec826000613bcc565b612bce613c17565b82612bd881613a74565b611dda8484846001613c58565b6002546001600160a01b031681565b612bfc613c17565b602554604080516024810185905260448101849052306064808301919091528251808303909101815260849091019091526020810180516001600160e01b031663561ad5c960e11b179052611cec916001600160a01b031690614c61565b60225481565b612c68613a07565b6005546016546040805163a3b1d8cb60e01b8152600481018590526024810192909252516001600160a01b039092169163a3b1d8cb9160448082019260009290919082900301818387803b158015612cbf57600080fd5b505af1158015612cd3573d6000803e3d6000fd5b505050506064811115612d17576040805162461bcd60e51b8152602060048201526007602482015266746f6f2062696760c81b604482015290519081900360640190fd5b601655565b612d24612b4c565b612d2d57600080fd5b602554604080516024810185905260448082018590528251808303909101815260649091019091526020810180516001600160e01b0316639ddbbf8560e01b179052611cec916001600160a01b031690614c61565b81612d8c8161445d565b6000821315612de55760195460ff16612de5576040805162461bcd60e51b815260206004820152601660248201527511195b1959d85d1a5bdb881a5cc8191a5cd8589b195960521b604482015290519081900360640190fd5b6000838152601a602052604090206003015480612e0d57612e0883600080613b10565b612e50565b600954811115612e50576040805162461bcd60e51b8152602060048201526009602482015268756e7374616b696e6760b81b604482015290519081900360640190fd5b60008312612e6757612e628484614ed8565b611dda565b611dda848460001902612ad2565b612e7d613a07565b80612e8757600080fd5b600e55565b601c5490565b612e9a613c17565b6025546040516024810187815260448201879052851515606483015260806084830190815260a4830185905261251e936001600160a01b03169263a685487760e01b928a928a928a928a928a9260c40184848082843760008184015260408051601f19601f9093018316909401848103909201845252506020810180516001600160e01b03199a909a166001600160e01b03909a169990991790985250614c6195505050505050565b612f4b613a07565b612f53614f13565b565b60085490565b60245481565b612f69613a07565b611cec82826001614ddf565b612f7d613a07565b600c91909155600d55565b6000818152601a60205260408120600301548190612fad57612fa98361422d565b5090505b6000838152601a602052604090206001908101546122319190612225908463ffffffff613afe16565b6000612fe58484846000614f1f565b949350505050565b612ff5613a07565b306001600160a01b0316816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561303857600080fd5b505afa15801561304c573d6000803e3d6000fd5b505050506040513d602081101561306257600080fd5b50516001600160a01b03161461307757600080fd5b6000918252601a602052604090912060060180546001600160a01b0319166001600160a01b03909216919091179055565b6000612fe58484846001614f1f565b6130bf613a07565b806130c957600080fd5b600554600b5460408051632dbe9d3160e21b8152600481018590526024810192909252516001600160a01b039092169163b6fa74c49160448082019260009290919082900301818387803b15801561312057600080fd5b505af1158015613134573d6000803e3d6000fd5b505050600b9190915550565b60145481565b60005460ff1681565b336000908152601b6020908152604080832054808452601a9092529091206006810154815460039092015460095461319493600160a01b90930460ff16929190613be0565b6131d5576040805162461bcd60e51b815260206004820152600d60248201526c3737ba103b30b634b230ba37b960991b604482015290519081900360640190fd5b6000818152601a60205260409020600601546001600160a01b03168061323b576040805162461bcd60e51b815260206004820152601660248201527511195b1959d85d1a5bdb881a5cc8191a5cd8589b195960521b604482015290519081900360640190fd5b604080516303dd464160e51b8152841515600482015290516001600160a01b03831691637ba8c82091602480830192600092919082900301818387803b15801561328457600080fd5b505af1158015613298573d6000803e3d6000fd5b50505050505050565b60216020526000908152604090205481565b806132bd81613a74565b611cec8260006150ae565b816132d281613a74565b6132db83614ec9565b602554604080516024810186905260448082018690528251808303909101815260649091019091526020810180516001600160e01b0316636e6cb15960e11b1790526127ca916001600160a01b031690614c61565b60285481565b601c54601d5482565b613347613a07565b6000811161335457600080fd5b600554600a5460408051635071805360e11b8152600481018590526024810192909252516001600160a01b039092169163a0e300a69160448082019260009290919082900301818387803b1580156133ab57600080fd5b505af11580156133bf573d6000803e3d6000fd5b505050600a8290555060088190556133de81600463ffffffff61528c16565b60158190556009546133f59163ffffffff613afe16565b60185550565b613403612b4c565b61340c57600080fd5b6001600160a01b03811661341f57600080fd5b6002546040516001600160a01b038084169216907f211c9015fc81c0dbd45bd99f0f29fc1c143bfd53442d5ffd722bbbef7a887fe990600090a3600280546001600160a01b0319166001600160a01b0392909216919091179055565b6000908152601a602052604090205490565b602b546001600160a01b031681565b6005546001600160a01b031681565b6134b3613a07565b6019805460ff1916911515919091179055565b6134ce612b4c565b6134d757600080fd5b612a13816152ae565b816134ea81613a74565b60006134f58361531d565b600954600f54600087815260216020526040902054929350909161351e9163ffffffff613afe16565b811015613560576040805162461bcd60e51b815260206004820152600b60248201526a139bdd08185b1b1bddd95960aa1b604482015290519081900360640190fd5b6000858152601a602090815260408083206005908101549054915163b80fbce560e01b8152600481018a81526001600160a01b039283166024830181905288841660448401526080606484019081528b5160848501528b519197949095169563b80fbce5958d9589958c958f95909460a490920192918601918190849084905b838110156135f85781810151838201526020016135e0565b50505050905090810190601f1680156136255780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b15801561364757600080fd5b505af115801561365b573d6000803e3d6000fd5b5050506000878152601a60205260409020600301541515905061368a57613681816153d6565b61368a836154e0565b6001600160a01b039081166000908152601b60209081526040808320600019905594909216808252848220889055968152601a825283812060050180546001600160a01b031916909717909655602190529320929092555050565b60235460ff161561372e576040805162461bcd60e51b815260206004820152600e60248201526d185b1c9958591e481a5b9a5d195960921b604482015290519081900360640190fd5b6023805460ff1916600117905561374481613a55565b61378e576040805162461bcd60e51b8152602060048201526016602482015275185d58dd1a5bdb881a5b5c1b081a5b98dbdc9c9958dd60521b604482015290519081900360640190fd5b602580546001600160a01b03199081166001600160a01b038481169190911790925560008054610100600160a81b031916610100878516021790556004805482168c84161790556002805482168b84161790556003805482168a84161790556006805482168984161790556005805482168884161790556007805490911691861691909117905561381e826152ae565b5050612000600855505060016009819055610376600a90815569044664c7bf6451f00000600b55670de0b6b3a7640000600c819055600d55610400600e556064600f55600760105560128290556108006015556016556019805460ff191690911790555050505050565b613890613a07565b6009546133f5908263ffffffff613afe16565b6138ab613a07565b612f536155fa565b6025546001600160a01b031681565b6007821161390b576040805162461bcd60e51b815260206004820152601160248201527024b73b30b634b21036b4b3b930ba34b7b760791b604482015290519081900360640190fd5b6000838152601a6020526040808220600601548151636e7ce59160e01b81523360048201526024810185905291516001600160a01b0390911692636e7ce591926044808201939182900301818387803b15801561396757600080fd5b505af115801561397b573d6000803e3d6000fd5b5050506000838152601a6020526040808220600601548151632830728d60e21b81523360048201526024810186905291516001600160a01b03909116935063a0c1ca349260448084019391929182900301818387803b15801561328457600080fd5b600d5481565b6003546001600160a01b031681565b806139fc81613a74565b611cec8260016150ae565b60005461010090046001600160a01b03163314612f535760405162461bcd60e51b8152600401808060200182810382526026815260200180615f6b6026913960400191505060405180910390fd5b60006001600160a01b038216613a6d57506000611a30565b503b151590565b600654604080516331a9108f60e11b815260048101849052905133926001600160a01b031691636352211e916024808301926020929190829003018186803b158015613abf57600080fd5b505afa158015613ad3573d6000803e3d6000fd5b505050506040513d6020811015613ae957600080fd5b50516001600160a01b031614612a1357600080fd5b60008282018381101561223157600080fd5b80613baa576000831315613b3957601c54613b31908463ffffffff613afe16565b601c55613b5d565b6000831215613b5d57601c54613b5990600085900363ffffffff6142a916565b601c555b6000821315613b8157601d54613b79908363ffffffff613afe16565b601d55613ba5565b6000821215613ba557601d54613ba190600084900363ffffffff6142a916565b601d555b6127ca565b6000818152601e60205260409020805484018155600101805483019055505050565b613bd582614ec9565b611cec823383615609565b60008084118015613bf85750821580613bf857508183115b80156124f257506001856003811115613c0d57fe5b1495945050505050565b60005460ff1615612f53576040805162461bcd60e51b81526020600482015260066024820152651b1bd8dad95960d21b604482015290519081900360640190fd5b6000848152601a602052604090206003015415613cab576040805162461bcd60e51b815260206004820152600c60248201526b4e6f2072657374616b696e6760a01b604482015290519081900360640190fd5b8215613cbd57613cbd333085846156e0565b613cc684614ec9565b8115613d0d576000848152601a60205260409020600190810154613cf6919061222590869063ffffffff613afe16565b6000858152601a6020526040902060019081015592505b601154600090613d23908563ffffffff613afe16565b60118190556000868152601a6020526040902054909150613d4a908563ffffffff613afe16565b6000868152601a6020526040812091909155613d6890859080613b10565b6005546040805163532e19a960e01b81526004810188905290516001600160a01b039092169163532e19a99160248082019260009290919082900301818387803b158015613db557600080fd5b505af1158015613dc9573d6000803e3d6000fd5b50506005546000888152601a6020526040808220548151631585a9f360e21b8152600481018c905260248101919091526044810187905290516001600160a01b039093169450635616a7cc93506064808201939182900301818387803b15801561238457600080fd5b600082815260208052604090205415613e4a57600080fd5b6000828152601a602052604090206006810154600290910154600160a01b90910460ff169015801590613e8c57506000838152601a6020526040902060030154155b8015613eb857506001816003811115613ea157fe5b1480613eb857506002816003811115613eb657fe5b145b613ec157600080fd5b600954600090613ed890600163ffffffff613afe16565b9050611dda8482856146f0565b805160208201516040830151600092919060ff167f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0821115613f2d5760009350505050611db5565b601b8160ff161015613f3d57601b015b8060ff16601b14158015613f5557508060ff16601c14155b15613f665760009350505050611db5565b60408051600080825260208083018085528a905260ff85168385015260608301879052608083018690529251909260019260a080820193601f1981019281900390910190855afa158015613fbe573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116613fde57600080fd5b9695505050505050565b613ff0615e9e565b826080015183602001511080156140345750816001600160a01b0316836060015184602001518151811061402057fe5b60200260200101516001600160a01b031614155b156140b557601b6000846060015185602001518151811061405157fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002054836040015184600001518151811061408e57fe5b60209081029190910181019190915283516001908101855290840180519091019052613ff0565b5050602081018051600101905290565b601c546000906140f2600161203a60036140e685600263ffffffff6157e116565b9063ffffffff61528c16565b88101561413c576040805162461bcd60e51b8152602060048201526013602482015272322f332b31206e6f6e2d6d616a6f726974792160681b604482015290519081900360640190fd5b60006141498b8a84615808565b9050600061416760646140e6601654856157e190919063ffffffff16565b6001600160a01b038c166000908152601b6020908152604080832054808452601a909252909120600181015492935090916141a8908463ffffffff613afe16565b600182015560178b905560006141f56141e68e6140e66a084595161401484a0000006141da8a8a63ffffffff6142a916565b9063ffffffff6157e116565b6024549063ffffffff613afe16565b90506142028b8b8361595c565b602481905561421289898361595c565b61421a615990565b50929d9c50505050505050505050505050565b6000818152601a602052604081208054600a90910154829190829061425990839063ffffffff613afe16565b6000868152601a60205260409020600b0154602454919250900361429d86846142976a084595161401484a0000006140e6868863ffffffff6157e116565b85615a05565b94509450505050915091565b6000828211156142b857600080fd5b50900390565b602b54604080516370a0823160e01b8152306004820152905183926001600160a01b0316916370a08231916024808301926020929190829003018186803b15801561430857600080fd5b505afa15801561431c573d6000803e3d6000fd5b505050506040513d602081101561433257600080fd5b50511015614377576040805162461bcd60e51b815260206004820152600d60248201526c4c61636b696e67204d4154494360981b604482015290519081900360640190fd5b602b54602c546040805163095ea7b360e01b81526001600160a01b039283166004820152602481018590529051919092169163095ea7b39160448083019260209291908290030181600087803b1580156143d057600080fd5b505af11580156143e4573d6000803e3d6000fd5b505050506040513d60208110156143fa57600080fd5b5050602c54604080516308a960c160e31b81526004810184905290516001600160a01b039092169163454b06089160248082019260009290919082900301818387803b15801561444957600080fd5b505af115801561251e573d6000803e3d6000fd5b6000818152601a60205260409020600601546001600160a01b03163314612a13576040805162461bcd60e51b815260206004820152601860248201527f496e76616c696420636f6e747261637420616464726573730000000000000000604482015290519081900360640190fd5b6000806144d783615a99565b604080516323b872dd60e01b81526001600160a01b038781166004830152306024830152604482018990529151929350600092918416916323b872dd9160648082019260209290919082900301818787803b15801561453557600080fd5b505af1158015614549573d6000803e3d6000fd5b505050506040513d602081101561455f57600080fd5b50519050836124f2576124f2866142be565b60105461457c612aaf565b106145be576040805162461bcd60e51b815260206004820152600d60248201526c6e6f206d6f726520736c6f747360981b604482015290519081900360640190fd5b600c5485101561460a576040805162461bcd60e51b81526020600482015260126024820152711b9bdd08195b9bdd59da0819195c1bdcda5d60721b604482015290519081900360640190fd5b6146178633868885614623565b61329886868585614854565b600d5483101561466a576040805162461bcd60e51b815260206004820152600d60248201526c199959481d1bdbc81cdb585b1b609a1b604482015290519081900360640190fd5b614685843061467f868663ffffffff613afe16565b846156e0565b602254614698908463ffffffff613afe16565b6022556005546040805163a449d79560e01b81526001600160a01b038881166004830152602482018790529151919092169163a449d79591604480830192600092919082900301818387803b15801561238457600080fd5b6146f983614ec9565b6000838152601a602052604081205490614712856127cf565b6000868152601a6020526040902060038101869055600a810154600690910154919250906001600160a01b0316801561479957806001600160a01b031663f83d08ba6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561478057600080fd5b505af1158015614794573d6000803e3d6000fd5b505050505b6000878152601a60205260409020600501546147bd906001600160a01b03166153d6565b6147c8878487615609565b60006009548711156147da57866147dd565b60005b90506147f183860160000360001983613b10565b60055460408051635e04d48360e01b81526001600160a01b038781166004830152602482018c9052604482018b90526064820189905291519190921691635e04d48391608480830192600092919082900301818387803b15801561262657600080fd5b6000806148608361531d565b600954601254600554601154939450919290916001600160a01b031690600090614890908a63ffffffff613afe16565b905080601181905550604051806101a001604052808a8152602001600181526020018581526020016000815260200160008152602001866001600160a01b03168152602001896148e157600061496d565b600754600480546040805163d4ad267360e01b81529283018990526001600160a01b03888116602485015291821660448401525192169163d4ad2673916064808201926020929091908290030181600087803b15801561494057600080fd5b505af1158015614954573d6000803e3d6000fd5b505050506040513d602081101561496a57600080fd5b50515b6001600160a01b031681526020016001815260006020808301829052604080840183905260016060808601829052608080870186905260245460a0978801528a8652601a855294839020875181559387015191840191909155908501516002830155840151600380830191909155918401516004820155918301516005830180546001600160a01b039283166001600160a01b03199182161790915560c08501516006850180549190931691161780825560e08501519260ff60a01b1990911690600160a01b908490811115614a3f57fe5b0217905550610100820151600782015561012082015160088201556101408201516009820155610160820151600a82015561018090910151600b9091015560008381526021602052604080822086905560065481516340c10f1960e01b81526001600160a01b038e8116600483015260248201889052925192909116926340c10f199260448084019382900301818387803b158015614add57600080fd5b505af1158015614af1573d6000803e3d6000fd5b5050506001600160a01b0386166000908152601b60205260408120859055614b1e91508a90600190613b10565b836020600085815260200190815260200160002060010181905550816001600160a01b03166333a8383c868986888e876040518763ffffffff1660e01b815260040180876001600160a01b03166001600160a01b0316815260200180602001868152602001858152602001848152602001838152602001828103825287818151815260200191508051906020019080838360005b83811015614bca578181015183820152602001614bb2565b50505050905090810190601f168015614bf75780820380516001836020036101000a031916815260200191505b50975050505050505050600060405180830381600087803b158015614c1b57600080fd5b505af1158015614c2f573d6000803e3d6000fd5b50505050614c47600184613afe90919063ffffffff16565b601255614c53856154e0565b509098975050505050505050565b600080825160208401856127105a03f43d604051816000823e828015614c85578282f35b8282fd5b60006020825181614c9657fe5b0615614ce0576040805162461bcd60e51b8152602060048201526014602482015273092dcecc2d8d2c840e0e4dedecc40d8cadccee8d60631b604482015290519081900360640190fd5b60006020835181614ced57fe5b0490508060020a8510614d3f576040805162461bcd60e51b81526020600482015260156024820152744c65616620696e64657820697320746f6f2062696760581b604482015290519081900360640190fd5b60008660205b85518111614dd15785810151925060028806614d915781836040516020018083815260200182815260200192505050604051602081830303815290604052805190602001209150614dc3565b828260405160200180838152602001828152602001925050506040516020818303038152906040528051906020012091505b600288049750602001614d45565b509094149695505050505050565b80614ded57614ded82615ac2565b6000614df882615a99565b9050806001600160a01b031663a9059cbb85856040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015614e5a57600080fd5b505af1158015614e6e573d6000803e3d6000fd5b505050506040513d6020811015614e8457600080fd5b5051611dda576040805162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015290519081900360640190fd5b612a1381602454602454615c4b565b6000828152601a60205260409020600a0154614efa908263ffffffff613afe16565b6000928352601a6020526040909220600a019190915550565b6000805460ff19169055565b6000848152601a60205260408120600601546001600160a01b0316331480614fbe57506004805460408051631ab0168360e31b8152905133936001600160a01b039093169263d580b41892808201926020929091829003018186803b158015614f8757600080fd5b505afa158015614f9b573d6000803e3d6000fd5b505050506040513d6020811015614fb157600080fd5b50516001600160a01b0316145b614ffd576040805162461bcd60e51b815260206004820152600b60248201526a1b9bdd08185b1b1bddd95960aa1b604482015290519081900360640190fd5b8161500b5761500b84615ac2565b600061501683615a99565b9050806001600160a01b031663a9059cbb85876040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561507857600080fd5b505af115801561508c573d6000803e3d6000fd5b505050506040513d60208110156150a257600080fd5b50519695505050505050565b6000828152601a602052604090206003015480158015906150e457506009546008546150e190839063ffffffff613afe16565b11155b8015615117575060036000848152601a6020526040902060060154600160a01b900460ff16600381111561511457fe5b14155b61512057600080fd5b6000838152601a6020526040812054601154909190615145908363ffffffff6142a916565b60118190559050615157853386615609565b60065460408051630852cd8d60e31b81526004810188905290516001600160a01b03909216916342966c689160248082019260009290919082900301818387803b1580156151a457600080fd5b505af11580156151b8573d6000803e3d6000fd5b5050506000868152601a6020526040812081815560048101919091556005810180546001600160a01b03191690556000197f584f46c60af19681376031579adb04a2416e54ee5505351c2a8435e3766026ea55600601805460ff60a01b1916600360a01b1790555061522b338386614ddf565b6005546040805163ae2e26b160e01b815233600482015260248101889052604481018590526064810184905290516001600160a01b039092169163ae2e26b19160848082019260009290919082900301818387803b15801561238457600080fd5b600080821161529a57600080fd5b60008284816152a557fe5b04949350505050565b6001600160a01b0381166152c157600080fd5b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000815160401461535f576040805162461bcd60e51b81526020600482015260076024820152663737ba10383ab160c91b604482015290519081900360640190fd5b815160208301206001600160a01b0381161580159061539457506001600160a01b0381166000908152601b6020526040902054155b611db5576040805162461bcd60e51b815260206004820152600e60248201526d24b73b30b634b21039b4b3b732b960911b604482015290519081900360640190fd5b602680549060009060001983018381106153ec57fe5b600091825260209091200154602680546001600160a01b03909216925090600019840190811061541857fe5b600091825260209091200180546001600160a01b031916905560001982015b80156154cf57836001600160a01b0316826001600160a01b0316141561545c576154cf565b6026600182038154811061546c57fe5b9060005260206000200160009054906101000a90046001600160a01b0316826026600184038154811061549b57fe5b600091825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055915060001901615437565b506000198201611dda602682615eee565b6026805460018101825560008290527f744a2cf8fd7008e3d53b67916e73460df9fa5214e3ef23dd4259ca09493a35940180546001600160a01b0319166001600160a01b0384161790555460001901805b80156155b25760006026600183038154811061554957fe5b6000918252602090912001546001600160a01b039081169150841681101561557157506155b2565b806026838154811061557f57fe5b600091825260209091200180546001600160a01b0319166001600160a01b03929092169190911790555060001901615531565b8181146127ca5782602682815481106155c757fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550505050565b6000805460ff19166001179055565b6000838152601a6020526040812060019081015461562c9163ffffffff6142a916565b601454909150615642908263ffffffff613afe16565b6014556000848152601a60205260409020600190810155615664838284614ddf565b60055460145460408051635b42d93560e11b815260048101889052602481018590526044810192909252516001600160a01b039092169163b685b26a9160648082019260009290919082900301818387803b1580156156c257600080fd5b505af11580156156d6573d6000803e3d6000fd5b5050505050505050565b60006156eb82615a99565b604080516323b872dd60e01b81526001600160a01b0388811660048301528781166024830152604482018790529151929350908316916323b872dd916064808201926020929091908290030181600087803b15801561574957600080fd5b505af115801561575d573d6000803e3d6000fd5b505050506040513d602081101561577357600080fd5b50516157bd576040805162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b604482015290519081900360640190fd5b811580156157d357506001600160a01b03841630145b1561251e5761251e836142be565b6000826157f057506000611db5565b828202828482816157fd57fe5b041461223157600080fd5b600e54600b54600091908261582883888161581f57fe5b04602954615d1b565b905060008111801561583c57506027548114155b1561587d57602754156158775760006064602a5484028161585957fe5b04905081602754111561586f5791820191615875565b80830392505b505b60278190555b600083881115615916576028546158c56158b560646140e66158a860026000198901890204866157e1565b889063ffffffff6157e116565b612225868663ffffffff6157e116565b91506158e76158da848763ffffffff6157e116565b8a9063ffffffff6142a916565b985061591261590560646140e6846141da898963ffffffff6157e116565b859063ffffffff6142a916565b9350505b61593a61592d856140e68b8763ffffffff6157e116565b829063ffffffff613afe16565b9050615950866140e6838a63ffffffff6157e116565b98975050505050505050565b60245460005b8381101561251e5761598885828151811061597957fe5b60200260200101518385615c4b565b600101615962565b60095460006159a682600163ffffffff613afe16565b90506159b0615f12565b506000818152601e602090815260408083208151808301909252805480835260019091015492820183905290926159e79290613b10565b506000918252601e6020526040822082815560010191909155600955565b60008082615a1857506000905080615a90565b6000615a2e846140e6888863ffffffff6157e116565b6000888152601a60205260409020600701549091508015615a7557615a72615a6560646140e6846141da8b8863ffffffff6142a916565b839063ffffffff613afe16565b91505b6000615a87878463ffffffff6142a916565b92945091925050505b94509492505050565b600081615ab157602b546001600160a01b0316611db5565b50506003546001600160a01b031690565b600354604080516370a0823160e01b8152306004820152905183926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015615b0c57600080fd5b505afa158015615b20573d6000803e3d6000fd5b505050506040513d6020811015615b3657600080fd5b50511015615b79576040805162461bcd60e51b815260206004820152600b60248201526a131858dada5b99c81413d360aa1b604482015290519081900360640190fd5b600354602c546040805163095ea7b360e01b81526001600160a01b039283166004820152602481018590529051919092169163095ea7b39160448083019260209291908290030181600087803b158015615bd257600080fd5b505af1158015615be6573d6000803e3d6000fd5b505050506040513d6020811015615bfc57600080fd5b5050602c546040805163efeff0c160e01b81526004810184905290516001600160a01b039092169163efeff0c19160248082019260009290919082900301818387803b15801561444957600080fd5b6000838152601a60205260409020600301548015801590615c6e57508060095410155b15615c7957506127ca565b6000848152601a60205260409020600b015483811015615cf8576000858152601a602052604090208054600a909101548015615ce0576000615cc1838363ffffffff613afe16565b9050615cda888484615cd58c868d8b615d31565b615d55565b50615cf5565b615cf587615cf089858a88615d31565b615dfb565b50505b8083111561251e576000858152601a60205260409020600b018390555050505050565b6000818310615d2a5781612231565b5090919050565b6000818303613fde6a084595161401484a0000006140e6838863ffffffff6157e116565b6000615d67838563ffffffff613afe16565b9050600080615d7887878686615a05565b90925090508015615db8576000878152601a6020526040902060090154615da5908263ffffffff613afe16565b6000888152601a60205260409020600901555b8115613298576000878152601a6020526040902060010154615de0908363ffffffff613afe16565b6000888152601a602052604090206001015550505050505050565b8015611cec576000828152601a6020526040902060010154615e23908263ffffffff613afe16565b6000838152601a60205260409020600101555050565b828054828255906000526020600020908101928215615e8e579160200282015b82811115615e8e57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190615e59565b50615e9a929150615f2c565b5090565b6040518060a0016040528060008152602001600081526020016060815260200160608152602001600081525090565b60405180606001604052806000815260200160608152602001600081525090565b8154818355818111156127ca576000838152602090206127ca918101908301615f50565b604051806040016040528060008152602001600081525090565b61275c91905b80821115615e9a5780546001600160a01b0319168155600101615f32565b61275c91905b80821115615e9a5760008155600101615f5656fe4f6e6c7920676f7665726e616e636520636f6e747261637420697320617574686f72697a6564a265627a7a723158202144f16fe19a38e4b93eaa1e2c04f33e2a9f779412c9defdf18d621af20c242664736f6c63430005110032", + "nonce": "0x8c", + "chainId": "0xaa36a7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xae07c932b4a5459bd683767dde161e9fa7ad1cc9b0ac83a39f9c291bcb22b79f", + "transactionType": "CREATE", + "contractName": null, + "contractAddress": "0x19b0b3256b22116096a2ef8cafc6e832ba18a947", + "function": null, + "arguments": null, + "transaction": { + "from": "0x0724d72eb61e508d81ca701881f2248f092953bf", + "gas": "0x3b3afb", + "value": "0x0", + "input": "0x60806040526003805460ff60a81b191690553480156200001e57600080fd5b5060038054610100338102610100600160a81b0319909216919091179182905560405191046001600160a01b0316906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3620000886001600160e01b036200008e16565b620000a3565b6003805460ff60a81b1916600160a81b179055565b61349680620000b36000396000f3fe608060405234801561001057600080fd5b50600436106103c55760003560e01c80638759c234116101ff578063b6658d071161011a578063e0db556b116100ad578063e97fddc21161007c578063e97fddc2146109aa578063eaf43bb5146109c7578063f2fde38b14610a05578063f83d08ba14610a2b576103c5565b8063e0db556b14610936578063e4457a8a1461093e578063e4cd1aec14610961578063e570b78b14610987576103c5565b8063c83ec04d116100e9578063c83ec04d146108d5578063cf309012146108f8578063dd62ed3e14610900578063df5cf7231461092e576103c5565b8063b6658d071461089a578063b89b63c1146108a2578063bfb18f29146108c5578063c7b8981c146108cd576103c5565b80639b2cb5d811610192578063a9059cbb11610161578063a9059cbb146107d8578063abf59fc914610804578063b4988fd01461083a578063b60c76fa1461086e576103c5565b80639b2cb5d814610770578063a0c1ca3414610778578063a457c2d7146107a4578063a69df4b5146107d0576103c5565b80638d16a14a116101ce5780638d16a14a146107505780638da5cb5b146107585780638f32d59b146107605780639ad5f5a414610768576103c5565b80638759c2341461071b578063883b455f146107385780638ccdd289146107405780638d086da414610748576103c5565b80634f91440d116102ef5780636ab1507111610282578063715018a611610251578063715018a6146106c05780637542ff95146106c8578063795be587146106d05780637ba8c820146106fc576103c5565b80636ab15071146106225780636cbb6050146106455780636e7ce5911461066e57806370a082311461069a576103c5565b80635f0c80cc116102be5780635f0c80cc146105a35780636035083f146105ab578063653ec134146105d6578063676e5550146105fc576103c5565b80634f91440d1461056c5780635adc6dde146105745780635c42c733146105935780635c5f7dae1461059b576103c5565b806323b872dd116103675780633a09bf44116103365780633a09bf44146105305780633ba0b9a9146105385780633d94eb0514610540578063446a2ec814610564576103c5565b806323b872dd146104a05780633046c204146104d657806339509351146104fc57806339c31e9314610528576103c5565b80631bf494a7116103a35780631bf494a7146104495780631e7ff8f614610451578063232e5415146104905780632344067914610498576103c5565b8063029d3040146103ca578063095ea7b3146103ef57806318160ddd1461042f575b600080fd5b6103ed600480360360408110156103e057600080fd5b5080359060200135610a33565b005b61041b6004803603604081101561040557600080fd5b506001600160a01b038135169060200135610a43565b604080519115158252519081900360200190f35b610437610a5a565b60408051918252519081900360200190f35b610437610a61565b6104776004803603602081101561046757600080fd5b50356001600160a01b0316610aed565b6040805192835260208301919091528051918290030190f35b6103ed610b4d565b610437610b59565b61041b600480360360608110156104b657600080fd5b506001600160a01b03813581169160208101359091169060400135610b5f565b610437600480360360208110156104ec57600080fd5b50356001600160a01b0316610bb7565b61041b6004803603604081101561051257600080fd5b506001600160a01b038135169060200135610bc9565b610437610c05565b610437610c0b565b610437610c11565b610548610cce565b604080516001600160a01b039092168252519081900360200190f35b610437610cdd565b610477610ce3565b6104776004803603602081101561058a57600080fd5b50351515610cf8565b610437610ff1565b610437610ff7565b610437610ffd565b6103ed600480360360608110156105c157600080fd5b50803590602081013590604001351515611003565b610477600480360360208110156105ec57600080fd5b50356001600160a01b03166111fe565b6104376004803603602081101561061257600080fd5b50356001600160a01b0316611217565b6104376004803603604081101561063857600080fd5b508035906020013561122a565b6104376004803603606081101561065b57600080fd5b5080359060208101359060400135611238565b6103ed6004803603604081101561068457600080fd5b506001600160a01b03813516906020013561136f565b610437600480360360208110156106b057600080fd5b50356001600160a01b03166115fb565b6103ed611616565b610548611677565b610477600480360360408110156106e657600080fd5b506001600160a01b038135169060200135611686565b6103ed6004803603602081101561071257600080fd5b503515156116aa565b6103ed6004803603602081101561073157600080fd5b50356116ce565b6105486116dc565b6104376116eb565b6104376116f1565b6103ed6116f7565b610548611701565b61041b611715565b61047761172b565b610437611738565b6103ed6004803603604081101561078e57600080fd5b506001600160a01b03813516906020013561173e565b61041b600480360360408110156107ba57600080fd5b506001600160a01b03813516906020013561176c565b6103ed6117a8565b61041b600480360360408110156107ee57600080fd5b506001600160a01b0381351690602001356117c1565b6103ed6004803603606081101561081a57600080fd5b506001600160a01b038135811691602081013590911690604001356117cf565b6103ed6004803603606081101561085057600080fd5b508035906001600160a01b03602082013581169160400135166118f6565b61041b6004803603604081101561088457600080fd5b506001600160a01b0381351690602001356119be565b6105486119cc565b6103ed600480360360408110156108b857600080fd5b50803590602001356119db565b6104376119e7565b6103ed611a2e565b6103ed600480360360408110156108eb57600080fd5b5080359060200135611a38565b61041b611a44565b6104376004803603604081101561091657600080fd5b506001600160a01b0381358116916020013516611a4d565b61041b611a78565b6103ed611a81565b6104376004803603604081101561095457600080fd5b5080359060200135611a8b565b6104376004803603602081101561097757600080fd5b50356001600160a01b0316611a99565b6103ed6004803603604081101561099d57600080fd5b5080359060200135611aab565b6103ed600480360360208110156109c057600080fd5b5035611ab7565b610437600480360360c08110156109dd57600080fd5b5080359060208101359060408101359060ff6060820135169060808101359060a00135611ac2565b6103ed60048036036020811015610a1b57600080fd5b50356001600160a01b0316611cab565b6103ed611cc5565b610a3f82826000611cde565b5050565b6000610a50338484611e8e565b5060015b92915050565b6002545b90565b6005546006546040805163072c21ef60e31b8152600481019290925251600092610ae8926001600160a01b03909116916339610f7891602480820192602092909190829003018186803b158015610ab757600080fd5b505afa158015610acb573d6000803e3d6000fd5b505050506040513d6020811015610ae157600080fd5b5051611ec6565b905090565b6000806000610afb846115fb565b90506000610b07610c11565b905081610b1b57600093509150610b489050565b610b42610b26611f1d565b610b36838563ffffffff611f4416565b9063ffffffff611f6b16565b93509150505b915091565b610b576001611f8d565b565b60095481565b6000610b6c84848461205a565b6001600160a01b038416600090815260016020908152604080832033808552925290912054610bac918691610ba7908663ffffffff61212516565b611e8e565b5060015b9392505050565b60146020526000908152604090205481565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610a50918590610ba7908663ffffffff61213a16565b60075481565b600d5481565b600080610c1c610a5a565b90506000610c28611f1d565b90508115610cc55760055460065460408051637f4b432360e01b8152600481019290925251610cc0928592610b369286926001600160a01b031691637f4b4323916024808301926020929190829003018186803b158015610c8857600080fd5b505afa158015610c9c573d6000803e3d6000fd5b505050506040513d6020811015610cb257600080fd5b50519063ffffffff611f4416565b610cc7565b805b9250505090565b6004546001600160a01b031681565b600c5481565b600080610cf06000610cf8565b915091509091565b6000803381610d068261214c565b90506000600a54821015610d61576040805162461bcd60e51b815260206004820152601c60248201527f546f6f20736d616c6c207265776172647320746f2072657374616b6500000000604482015290519081900360640190fd5b8115610b4257610d73826000856121fa565b905080821115610f615785610e14576005546006546040805163bc8756a960e01b8152600481019290925283850360248301526001600160a01b038681166044840152905192169163bc8756a9916064808201926020929091908290030181600087803b158015610de357600080fd5b505af1158015610df7573d6000803e3d6000fd5b505050506040513d6020811015610e0d57600080fd5b5051610ea2565b6005546006546040805163c7f067cb60e01b8152600481019290925283850360248301526001600160a01b038681166044840152905192169163c7f067cb916064808201926020929091908290030181600087803b158015610e7557600080fd5b505af1158015610e89573d6000803e3d6000fd5b505050506040513d6020811015610e9f57600080fd5b50515b610ee9576040805162461bcd60e51b8152602060048201526013602482015272496e737566666963656e74207265776172647360681b604482015290519081900360640190fd5b600480546006546040805163b7721d2d60e01b8152938401919091526001600160a01b0386811660248501528486036044850152905191169163b7721d2d91606480830192600092919082900301818387803b158015610f4857600080fd5b505af1158015610f5c573d6000803e3d6000fd5b505050505b6000610f6c84610aed565b506004805460065460408051637f88a95760e01b8152938401919091526001600160a01b0388811660248501526044840185905290519394501691637f88a9579160648082019260009290919082900301818387803b158015610fce57600080fd5b505af1158015610fe2573d6000803e3d6000fd5b50505050509350915050915091565b600f5481565b60065481565b600b5481565b6000806110118585856124b9565b336000908152601460205260408120549294509092509061103990600163ffffffff61213a16565b9050611043613447565b6040518060400160405280848152602001600560009054906101000a90046001600160a01b03166001600160a01b031663900cf0cf6040518163ffffffff1660e01b815260040160206040518083038186803b1580156110a257600080fd5b505afa1580156110b6573d6000803e3d6000fd5b505050506040513d60208110156110cc57600080fd5b50519052336000818152601560209081526040808320878452825280832085518155858301516001909101559282526014905220839055905061110d612685565b60065460408051632a9b5b4160e11b81526004810192909252336024830152604482018a90526064820187905260848201859052516001600160a01b039290921691635536b6829160a48082019260009290919082900301818387803b15801561117657600080fd5b505af115801561118a573d6000803e3d6000fd5b5050600480546006546040805163532e19a960e01b815293840191909152516001600160a01b03909116935063532e19a99250602480830192600092919082900301818387803b1580156111dd57600080fd5b505af11580156111f1573d6000803e3d6000fd5b5050505050505050505050565b6012602052600090815260409020805460019091015482565b6000610a5482611225610a61565b6127c2565b6000610bb083836000612843565b6000611242611715565b61124b57600080fd5b600f546000611260858363ffffffff61213a16565b90508061127257600092505050610bb0565b6000611297611287888463ffffffff61213a16565b610b36848863ffffffff611f4416565b905060006112af83610b36868563ffffffff611f4416565b905060006112c3838363ffffffff61212516565b6005546006546040805163858a7c0360e01b8152600481019290925260248201849052519293506001600160a01b039091169163858a7c039160448082019260009290919082900301818387803b15801561131d57600080fd5b505af1158015611331573d6000803e3d6000fd5b5050600d54611349925090508263ffffffff61212516565b600d55600f5461135f908363ffffffff61212516565b600f555090979650505050505050565b611377611715565b61138057600080fd5b61138b8260016129bd565b5060008061139884610aed565b91509150828210156113e6576040805162461bcd60e51b815260206004820152601260248201527109ad2cee4c2e8d2dcce40e8dede40daeac6d60731b604482015290519081900360640190fd5b60006113f0611f1d565b9050600061140883610b36878563ffffffff611f4416565b90506114148682612bb9565b60055460065460408051629ff11560e81b815260048101929092526000888103602484015290516001600160a01b0390931692639ff1150092604480820193929182900301818387803b15801561146a57600080fd5b505af115801561147e573d6000803e3d6000fd5b5050600d54611496925090508663ffffffff61212516565b600d55600480546006546040805163f1382b5360e01b8152938401919091526001600160a01b0389811660248501526044840189905260648401859052905191169163f1382b5391608480830192600092919082900301818387803b1580156114fe57600080fd5b505af1158015611512573d6000803e3d6000fd5b5050600480546006546040805163532e19a960e01b815293840191909152516001600160a01b03909116935063532e19a99250602480830192600092919082900301818387803b15801561156557600080fd5b505af1158015611579573d6000803e3d6000fd5b5050600480546006546040805163302df4df60e11b8152938401919091526001600160a01b038b81166024850152604484018b905290519116935063605be9be9250606480830192600092919082900301818387803b1580156115db57600080fd5b505af11580156115ef573d6000803e3d6000fd5b50505050505050505050565b6001600160a01b031660009081526020819052604090205490565b61161e611715565b61162757600080fd5b60035460405160009161010090046001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a360038054610100600160a81b0319169055565b6005546001600160a01b031681565b60156020908152600092835260408084209091529082529020805460019091015482565b6116b2611715565b6116bb57600080fd5b600e805460ff1916911515919091179055565b6116d9816001612c60565b50565b6016546001600160a01b031681565b60085481565b60105481565b610b576000611f8d565b60035461010090046001600160a01b031690565b60035461010090046001600160a01b0316331490565b600080610cf06001610cf8565b600a5481565b611746611715565b61174f57600080fd5b61175a8260016129bd565b50611767816000846121fa565b505050565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610a50918590610ba7908663ffffffff61212516565b6117b0611715565b6117b957600080fd5b610b57612d50565b6000610a5083836000612d5c565b6117d7611715565b6117e057600080fd5b6001600160a01b03831661182a576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015611824573d6000803e3d6000fd5b50611767565b826001600160a01b031663a9059cbb83836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561188a57600080fd5b505af115801561189e573d6000803e3d6000fd5b505050506040513d60208110156118b457600080fd5b5051611767576040805162461bcd60e51b815260206004820152600c60248201526b111c985a5b8819985a5b195960a21b604482015290519081900360640190fd5b600354600160a81b900460ff1615611946576040805162461bcd60e51b815260206004820152600e60248201526d185b1c9958591e481a5b9a5d195960921b604482015290519081900360640190fd5b6003805460ff60a81b1916600160a81b1790556006839055600480546001600160a01b038085166001600160a01b031992831617909255600580549284169290911691909117905561199781612dea565b61199f612685565b5050670de0b6b3a7640000600a555050600e805460ff19166001179055565b6000610a5083836001612d5c565b6017546001600160a01b031681565b610a3f82826001611cde565b6000806119f2611f1d565b905060086006541015611a06579050610a5e565b6010548015611a2857610cc081610b3684600f54611f4490919063ffffffff16565b50919050565b610b576000612e64565b610a3f82826000611003565b60035460ff1681565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600e5460ff1681565b610b576001612e64565b6000610bb083836001612843565b60136020526000908152604090205481565b610a3f82826001611003565b6116d9816000612c60565b600080611acd612ec9565b60408051623f675f60e91b815233600482015290519192506000916001600160a01b03841691637ecebe00916024808301926020929190829003018186803b158015611b1857600080fd5b505afa158015611b2c573d6000803e3d6000fd5b505050506040513d6020811015611b4257600080fd5b50516005546040805163d505accf60e01b81523360048201526001600160a01b039283166024820152604481018d9052606481018b905260ff8a16608482015260a4810189905260c4810188905290519293509084169163d505accf9160e48082019260009290919082900301818387803b158015611bc057600080fd5b505af1158015611bd4573d6000803e3d6000fd5b505060408051623f675f60e91b815233600482015290516001850193506001600160a01b0386169250637ecebe0091602480820192602092909190829003018186803b158015611c2357600080fd5b505afa158015611c37573d6000803e3d6000fd5b505050506040513d6020811015611c4d57600080fd5b505114611c92576040805162461bcd60e51b815260206004820152600e60248201526d125b9d985b1a59081c195c9b5a5d60921b604482015290519081900360640190fd5b611c9e89896001612843565b9998505050505050505050565b611cb3611715565b611cbc57600080fd5b6116d981612dea565b611ccd611715565b611cd657600080fd5b610b57613043565b600080611cec8585856124b9565b91509150611cf8613447565b50336000908152601260209081526040918290208251808401909352805480845260019091015491830191909152611d36908363ffffffff61213a16565b81526005546040805163900cf0cf60e01b815290516001600160a01b039092169163900cf0cf91600480820192602092909190829003018186803b158015611d7d57600080fd5b505afa158015611d91573d6000803e3d6000fd5b505050506040513d6020811015611da757600080fd5b505160208281019182523360008181526012909252604080832085518155935160019094019390935560048054600654855163f1382b5360e01b8152928301526024820192909252604481018a90526064810187905292516001600160a01b0390911692839263f1382b539260848084019382900301818387803b158015611e2e57600080fd5b505af1158015611e42573d6000803e3d6000fd5b50505050806001600160a01b031663532e19a96006546040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156111dd57600080fd5b6040805162461bcd60e51b8152602060048201526008602482015267191a5cd8589b195960c21b604482015290519081900360640190fd5b600c546000908215610a54576000611edc610a5a565b90508015611f1657611f13611f0682610b36876a084595161401484a00000063ffffffff611f4416565b839063ffffffff61213a16565b91505b5092915050565b600060086006541015611f3257506064610a5e565b506c01431e0fae6d7217caa000000090565b600082611f5357506000610a54565b82820282848281611f6057fe5b0414610bb057600080fd5b6000808211611f7957600080fd5b6000828481611f8457fe5b04949350505050565b611f95613447565b50336000908152601260209081526040808320815180830190925280548252600101549181019190915290611fca8284613052565b3360008181526012602052604080822082815560010182905560048054600654835163302df4df60e11b81529283015260248201949094526044810185905290519394506001600160a01b039092169263605be9be92606480820193929182900301818387803b15801561203d57600080fd5b505af1158015612051573d6000803e3d6000fd5b50505050505050565b6001600160a01b03821661206d57600080fd5b6001600160a01b038316600090815260208190526040902054612096908263ffffffff61212516565b6001600160a01b0380851660009081526020819052604080822093909355908416815220546120cb908263ffffffff61213a16565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60008282111561213457600080fd5b50900390565b600082820183811015610bb057600080fd5b6000806121c3600560009054906101000a90046001600160a01b03166001600160a01b0316637ed4b27c6006546040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156121af57600080fd5b505af1158015610acb573d6000803e3d6000fd5b905060006121d184836127c2565b600c8390556001600160a01b038516600090815260136020526040902092909255509050919050565b600061220461335e565b600e5460ff16612254576040805162461bcd60e51b815260206004820152601660248201527511195b1959d85d1a5bdb881a5cc8191a5cd8589b195960521b604482015290519081900360640190fd5b600061225e610c11565b9050600061226a611f1d565b9050600061228283610b36898563ffffffff611f4416565b9050858110156122cd576040805162461bcd60e51b8152602060048201526011602482015270546f6f206d75636820736c69707061676560781b604482015290519081900360640190fd5b6001600160a01b03851660009081526012602052604090205415612327576040805162461bcd60e51b815260206004820152600c60248201526b13db99dbda5b99c8195e1a5d60a21b604482015290519081900360640190fd5b612331858261339f565b61234582610b36858463ffffffff611f4416565b60055460065460408051629ff11560e81b8152600481019290925260248201849052519299506001600160a01b0390911691639ff115009160448082019260009290919082900301818387803b15801561239e57600080fd5b505af11580156123b2573d6000803e3d6000fd5b5050600d546123ca925090508863ffffffff61213a16565b600d55600480546006546040805163c69d057360e01b8152938401919091526001600160a01b038881166024850152604484018b9052606484018590529051911691829163c69d05739160848082019260009290919082900301818387803b15801561243557600080fd5b505af1158015612449573d6000803e3d6000fd5b50505050806001600160a01b031663532e19a96006546040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561249557600080fd5b505af11580156124a9573d6000803e3d6000fd5b50999a9950505050505050505050565b6000806000806124c833610aed565b91509150816000141580156124dd5750868210155b612523576040805162461bcd60e51b8152602060048201526012602482015271151bdbc81b5d58da081c995c5d595cdd195960721b604482015290519081900360640190fd5b600061252d611f1d565b9050600061254583610b368b8563ffffffff611f4416565b905087811115612590576040805162461bcd60e51b8152602060048201526011602482015270746f6f206d75636820736c69707061676560781b604482015290519081900360640190fd5b61259a33886129bd565b506125a53382612bb9565b60055460065460408051629ff11560e81b8152600481019290925260008c8103602484015290516001600160a01b0390931692639ff1150092604480820193929182900301818387803b1580156125fb57600080fd5b505af115801561260f573d6000803e3d6000fd5b5050600d54612627925090508a63ffffffff61212516565b600d5560006126476126376119e7565b610b368c8663ffffffff611f4416565b600f5490915061265d908b63ffffffff61213a16565b600f55601054612673908263ffffffff61213a16565b60105590999098509650505050505050565b6016546000906001600160a01b031680610ae857600560009054906101000a90046001600160a01b03166001600160a01b0316635ab1bd536040518163ffffffff1660e01b815260040160206040518083038186803b1580156126e757600080fd5b505afa1580156126fb573d6000803e3d6000fd5b505050506040513d602081101561271157600080fd5b5051604080516832bb32b73a39a43ab160b91b8152815190819003600901812063cac39a0560e01b8252600482015290516001600160a01b039092169163cac39a0591602480820192602092909190829003018186803b15801561277457600080fd5b505afa158015612788573d6000803e3d6000fd5b505050506040513d602081101561279e57600080fd5b5051601680546001600160a01b0319166001600160a01b0383161790559050905090565b6000806127ce846115fb565b9050806127df576000915050610a54565b6001600160a01b0384166000908152601360205260409020548381141561280b57600092505050610a54565b61283a6a084595161401484a000000610b368461282e888663ffffffff61212516565b9063ffffffff611f4416565b95945050505050565b600061284f33836129bd565b5061285b8484336121fa565b9050816128f15760055460065460408051636901b25360e01b8152600481019290925260248201849052336044830152516001600160a01b0390921691636901b253916064808201926020929091908290030181600087803b1580156128c057600080fd5b505af11580156128d4573d6000803e3d6000fd5b505050506040513d60208110156128ea57600080fd5b505161297b565b60055460065460408051624c025560e81b8152600481019290925260248201849052336044830152516001600160a01b0390921691634c025500916064808201926020929091908290030181600087803b15801561294e57600080fd5b505af1158015612962573d6000803e3d6000fd5b505050506040513d602081101561297857600080fd5b50515b610bb0576040805162461bcd60e51b815260206004820152600e60248201526d19195c1bdcda5d0819985a5b195960921b604482015290519081900360640190fd5b6000806129c98461214c565b90508015610bb05782612a67576005546006546040805163bc8756a960e01b81526004810192909252602482018490526001600160a01b038781166044840152905192169163bc8756a9916064808201926020929091908290030181600087803b158015612a3657600080fd5b505af1158015612a4a573d6000803e3d6000fd5b505050506040513d6020811015612a6057600080fd5b5051612af4565b6005546006546040805163c7f067cb60e01b81526004810192909252602482018490526001600160a01b038781166044840152905192169163c7f067cb916064808201926020929091908290030181600087803b158015612ac757600080fd5b505af1158015612adb573d6000803e3d6000fd5b505050506040513d6020811015612af157600080fd5b50515b612b3b576040805162461bcd60e51b8152602060048201526013602482015272496e737566666963656e74207265776172647360681b604482015290519081900360640190fd5b600480546006546040805163b7721d2d60e01b8152938401919091526001600160a01b03878116602485015260448401859052905191169163b7721d2d91606480830192600092919082900301818387803b158015612b9957600080fd5b505af1158015612bad573d6000803e3d6000fd5b50505050905092915050565b6001600160a01b038216612bcc57600080fd5b600254612bdf908263ffffffff61212516565b6002556001600160a01b038216600090815260208190526040902054612c0b908263ffffffff61212516565b6001600160a01b038316600081815260208181526040808320949094558351858152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35050565b612c68613447565b503360009081526015602090815260408083208584528252808320815180830190925280548252600101549181019190915290612ca58284613052565b3360009081526015602090815260408083208884529091528120818155600101559050612cd0612685565b60065460408051636e699d8760e01b815260048101929092523360248301526044820184905260648201879052516001600160a01b039290921691636e699d879160848082019260009290919082900301818387803b158015612d3257600080fd5b505af1158015612d46573d6000803e3d6000fd5b5050505050505050565b6003805460ff19169055565b33612d6784836129bd565b50612d7281836129bd565b50612d7e81858561205a565b612d86612685565b6006546040805163c08b3f9d60e01b815260048101929092526001600160a01b038481166024840152878116604484015260648301879052905192169163c08b3f9d9160848082019260009290919082900301818387803b158015612d3257600080fd5b6001600160a01b038116612dfd57600080fd5b6003546040516001600160a01b0380841692610100900416907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600380546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b6000612e7033836129bd565b9050600a54811015610a3f576040805162461bcd60e51b815260206004820152601860248201527f546f6f20736d616c6c207265776172647320616d6f756e740000000000000000604482015290519081900360640190fd5b6017546000906001600160a01b031680610ae857600560009054906101000a90046001600160a01b03166001600160a01b0316635ab1bd536040518163ffffffff1660e01b815260040160206040518083038186803b158015612f2b57600080fd5b505afa158015612f3f573d6000803e3d6000fd5b505050506040513d6020811015612f5557600080fd5b505160408051621c1bdb60ea1b8152815190819003600301812063cac39a0560e01b8252600482015290516001600160a01b039092169163cac39a0591602480820192602092909190829003018186803b158015612fb257600080fd5b505afa158015612fc6573d6000803e3d6000fd5b505050506040513d6020811015612fdc57600080fd5b505190506001600160a01b038116613023576040805162461bcd60e51b81526020600482015260056024820152641d5b9cd95d60da1b604482015290519081900360640190fd5b601780546001600160a01b0319166001600160a01b038316179055919050565b6003805460ff19166001179055565b81516005546040805163900cf0cf60e01b81529051600093926001600160a01b03169163900cf0cf916004808301926020929190829003018186803b15801561309a57600080fd5b505afa1580156130ae573d6000803e3d6000fd5b505050506040513d60208110156130c457600080fd5b50516005546040805163a7ab696160e01b81529051613149926001600160a01b03169163a7ab6961916004808301926020929190829003018186803b15801561310c57600080fd5b505afa158015613120573d6000803e3d6000fd5b505050506040513d602081101561313657600080fd5b505160208701519063ffffffff61213a16565b111580156131575750600081115b6131a8576040805162461bcd60e51b815260206004820152601c60248201527f496e636f6d706c657465207769746864726177616c20706572696f6400000000604482015290519081900360640190fd5b60006131c16131b5611f1d565b610b368461282e6119e7565b6010549091506131d7908363ffffffff61212516565b601055600f546131ed908263ffffffff61212516565b600f5583613284576005546006546040805163bc8756a960e01b8152600481019290925260248201849052336044830152516001600160a01b039092169163bc8756a9916064808201926020929091908290030181600087803b15801561325357600080fd5b505af1158015613267573d6000803e3d6000fd5b505050506040513d602081101561327d57600080fd5b505161330f565b6005546006546040805163c7f067cb60e01b8152600481019290925260248201849052336044830152516001600160a01b039092169163c7f067cb916064808201926020929091908290030181600087803b1580156132e257600080fd5b505af11580156132f6573d6000803e3d6000fd5b505050506040513d602081101561330c57600080fd5b50515b613356576040805162461bcd60e51b8152602060048201526013602482015272496e737566666963656e74207265776172647360681b604482015290519081900360640190fd5b949350505050565b60035460ff1615610b57576040805162461bcd60e51b81526020600482015260066024820152651b1bd8dad95960d21b604482015290519081900360640190fd5b6001600160a01b0382166133b257600080fd5b6002546133c5908263ffffffff61213a16565b6002556001600160a01b0382166000908152602081905260409020546133f1908263ffffffff61213a16565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b60405180604001604052806000815260200160008152509056fea265627a7a723158204218c2c8b2e87f177283c78647790865bea3a76c3c8e35d83898fc32476d052564736f6c63430005110032", + "nonce": "0x8d", + "chainId": "0xaa36a7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0xcf4a40", + "logs": [ + { + "address": "0x777badc2c75bd4ee5303bf6e90009bd31d8fd3d1", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000724d72eb61e508d81ca701881f2248f092953bf" + ], + "data": "0x", + "blockHash": "0x80bb5b7121caad8c640dafc4e50623df1bc4757fbe8f5cd035bd702d975119ec", + "blockNumber": "0x605f17", + "transactionHash": "0x4499d0ecf70c3a39927d5245cac653c02f0f0c33dc82be6a9ce5d37d4615c70a", + "transactionIndex": "0x40", + "logIndex": "0x73", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000002000001000000000000004000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000020000010000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x4499d0ecf70c3a39927d5245cac653c02f0f0c33dc82be6a9ce5d37d4615c70a", + "transactionIndex": "0x40", + "blockHash": "0x80bb5b7121caad8c640dafc4e50623df1bc4757fbe8f5cd035bd702d975119ec", + "blockNumber": "0x605f17", + "gasUsed": "0x526898", + "effectiveGasPrice": "0x3be653915", + "from": "0x0724d72eb61e508d81ca701881f2248f092953bf", + "to": null, + "contractAddress": "0x777badc2c75bd4ee5303bf6e90009bd31d8fd3d1" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xfcdd6b", + "logs": [ + { + "address": "0x19b0b3256b22116096a2ef8cafc6e832ba18a947", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000724d72eb61e508d81ca701881f2248f092953bf" + ], + "data": "0x", + "blockHash": "0x80bb5b7121caad8c640dafc4e50623df1bc4757fbe8f5cd035bd702d975119ec", + "blockNumber": "0x605f17", + "transactionHash": "0xae07c932b4a5459bd683767dde161e9fa7ad1cc9b0ac83a39f9c291bcb22b79f", + "transactionIndex": "0x41", + "logIndex": "0x74", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000100100400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000020000010000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xae07c932b4a5459bd683767dde161e9fa7ad1cc9b0ac83a39f9c291bcb22b79f", + "transactionIndex": "0x41", + "blockHash": "0x80bb5b7121caad8c640dafc4e50623df1bc4757fbe8f5cd035bd702d975119ec", + "blockNumber": "0x605f17", + "gasUsed": "0x2d932b", + "effectiveGasPrice": "0x3be653915", + "from": "0x0724d72eb61e508d81ca701881f2248f092953bf", + "to": null, + "contractAddress": "0x19b0b3256b22116096a2ef8cafc6e832ba18a947" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1721057742, + "chain": 11155111, + "commit": "a5fd7d69" +} \ No newline at end of file diff --git a/contracts/common/misc/EIP712.sol b/contracts/common/misc/EIP712.sol new file mode 100644 index 00000000..4b462141 --- /dev/null +++ b/contracts/common/misc/EIP712.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.5.17; + +/* + meant for testing only, adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.0.2/contracts/utils/cryptography/EIP712.sol + modifications: + - removed `immutable` since was it introduced in solidity 0.6.5 but we have to adhere to 0.5.17 + - added chainId method since block.chainid isn't available yet + - removed `_nameFallback` & `_versionFallback` +*/ + +contract EIP712 { + /* solhint-disable var-name-mixedcase */ + // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to + // invalidate the cached domain separator if the chain id changes. + bytes32 private _CACHED_DOMAIN_SEPARATOR; + uint256 private _CACHED_CHAIN_ID; + + bytes32 private _HASHED_NAME; + bytes32 private _HASHED_VERSION; + bytes32 private _TYPE_HASH; + + string private _VERSION; + /* solhint-enable var-name-mixedcase */ + + /** + * @dev Initializes the domain separator and parameter caches. + * + * The meaning of `name` and `version` is specified in + * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: + * + * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. + * - `version`: the current major version of the signing domain. + * + * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart + * contract upgrade]. + */ + constructor(string memory name, string memory version) public { + bytes32 hashedName = keccak256(bytes(name)); + bytes32 hashedVersion = keccak256(bytes(version)); + bytes32 typeHash = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + _HASHED_NAME = hashedName; + _HASHED_VERSION = hashedVersion; + _CACHED_CHAIN_ID = _chainId(); + _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); + _TYPE_HASH = typeHash; + } + + function version() external view returns (string memory) { + return _VERSION; + } + + function _chainId() internal pure returns (uint chainId) { + assembly { + chainId := chainid() + } + } + + /** + * @dev Returns the domain separator for the current chain. + */ + function _domainSeparatorV4() internal view returns (bytes32) { + if (_chainId() == _CACHED_CHAIN_ID) { + return _CACHED_DOMAIN_SEPARATOR; + } else { + return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); + } + } + + function _buildDomainSeparator( + bytes32 typeHash, + bytes32 nameHash, + bytes32 versionHash + ) private view returns (bytes32) { + return keccak256(abi.encode(typeHash, nameHash, versionHash, _chainId(), address(this))); + } + + /** + * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this + * function returns the hash of the fully encoded EIP712 message for this domain. + * + * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: + * + * ```solidity + * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( + * keccak256("Mail(address to,string contents)"), + * mailTo, + * keccak256(bytes(mailContents)) + * ))); + * address signer = ECDSA.recover(digest, signature); + * ``` + */ + function _hashTypedDataV4(bytes32 structHash) internal view returns (bytes32) { + return _toTypedDataHash(_domainSeparatorV4(), structHash); + } + + /** + * @dev Returns an Ethereum Signed Typed Data, created from a + * `domainSeparator` and a `structHash`. This produces hash corresponding + * to the one signed with the + * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] + * JSON-RPC method as part of EIP-712. + */ + function _toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + } +} diff --git a/contracts/common/misc/IERC20Permit.sol b/contracts/common/misc/IERC20Permit.sol new file mode 100644 index 00000000..9b6d9b23 --- /dev/null +++ b/contracts/common/misc/IERC20Permit.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.5.17; + +interface IERC20Permit { + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + function nonces(address owner) external view returns (uint256); + + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view returns (bytes32); +} diff --git a/contracts/common/misc/IPolygonMigration.sol b/contracts/common/misc/IPolygonMigration.sol new file mode 100644 index 00000000..02984c74 --- /dev/null +++ b/contracts/common/misc/IPolygonMigration.sol @@ -0,0 +1,10 @@ +pragma solidity 0.5.17; + +interface IPolygonMigration { + event Migrated(address indexed account, uint256 amount); + event Unmigrated(address indexed account, address indexed recipient, uint256 amount); + event UnmigrationLockUpdated(bool lock); + + function migrate(uint256 amount) external; + function unmigrate(uint256 amount) external; +} diff --git a/contracts/common/misc/ITimelock.sol b/contracts/common/misc/ITimelock.sol new file mode 100644 index 00000000..b965e9f4 --- /dev/null +++ b/contracts/common/misc/ITimelock.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +// extracted using cast interface 0xCaf0aa768A3AE1297DF20072419Db8Bb8b5C8cEf + +interface Timelock { + event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data); + event CallScheduled( + bytes32 indexed id, + uint256 indexed index, + address target, + uint256 value, + bytes data, + bytes32 predecessor, + uint256 delay + ); + event Cancelled(bytes32 indexed id); + event MinDelayChange(uint256 oldDuration, uint256 newDuration); + event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); + event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); + event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); + + receive() external payable; + + function DEFAULT_ADMIN_ROLE() external view returns (bytes32); + function EXECUTOR_ROLE() external view returns (bytes32); + function PROPOSER_ROLE() external view returns (bytes32); + function TIMELOCK_ADMIN_ROLE() external view returns (bytes32); + function cancel(bytes32 id) external; + function execute(address target, uint256 value, bytes memory data, bytes32 predecessor, bytes32 salt) + external + payable; + function executeBatch( + address[] memory targets, + uint256[] memory values, + bytes[] memory datas, + bytes32 predecessor, + bytes32 salt + ) external payable; + function getMinDelay() external view returns (uint256 duration); + function getRoleAdmin(bytes32 role) external view returns (bytes32); + function getTimestamp(bytes32 id) external view returns (uint256 timestamp); + function grantRole(bytes32 role, address account) external; + function hasRole(bytes32 role, address account) external view returns (bool); + function hashOperation(address target, uint256 value, bytes memory data, bytes32 predecessor, bytes32 salt) + external + pure + returns (bytes32 hash); + function hashOperationBatch( + address[] memory targets, + uint256[] memory values, + bytes[] memory datas, + bytes32 predecessor, + bytes32 salt + ) external pure returns (bytes32 hash); + function isOperation(bytes32 id) external view returns (bool pending); + function isOperationDone(bytes32 id) external view returns (bool done); + function isOperationPending(bytes32 id) external view returns (bool pending); + function isOperationReady(bytes32 id) external view returns (bool ready); + function renounceRole(bytes32 role, address account) external; + function revokeRole(bytes32 role, address account) external; + function schedule( + address target, + uint256 value, + bytes memory data, + bytes32 predecessor, + bytes32 salt, + uint256 delay + ) external; + function scheduleBatch( + address[] memory targets, + uint256[] memory values, + bytes[] memory datas, + bytes32 predecessor, + bytes32 salt, + uint256 delay + ) external; + function supportsInterface(bytes4 interfaceId) external view returns (bool); + function updateDelay(uint256 newDelay) external; +} diff --git a/contracts/common/misc/PolygonMigration.sol b/contracts/common/misc/PolygonMigration.sol new file mode 100644 index 00000000..d7e78419 --- /dev/null +++ b/contracts/common/misc/PolygonMigration.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.5.17; + +import {IERC20} from "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol"; +import {IPolygonMigration} from "./IPolygonMigration.sol"; + +/// @title Polygon Migration +/// @author Polygon Labs (@DhairyaSethi, @gretzke, @qedk) +/// @notice This is the migration contract for Matic <-> Polygon ERC20 token on Ethereum L1 +/// @dev The contract allows for a 1-to-1 conversion from $MATIC into $POL and vice-versa +contract PolygonMigration is IPolygonMigration { + using SafeERC20 for IERC20; + + IERC20 public legacy; + IERC20 public staking; + address owner; + + bool public unmigrationLocked; + + modifier onlyUnmigrationUnlocked() { + require(!unmigrationLocked, "UnmigrationLocked"); + _; + } + + modifier onlyOwner() { + require(msg.sender == owner, "Only owner"); + _; + } + + constructor(address _legacy, address _staking) public { + staking = IERC20(_staking); + legacy = IERC20(_legacy); + owner = msg.sender; + unmigrationLocked = false; + } + + function migrate(uint256 amount) external { + emit Migrated(msg.sender, amount); + + legacy.safeTransferFrom(msg.sender, address(this), amount); + staking.safeTransfer(msg.sender, amount); + } + + function unmigrate(uint256 amount) external onlyUnmigrationUnlocked { + emit Unmigrated(msg.sender, msg.sender, amount); + + staking.safeTransferFrom(msg.sender, address(this), amount); + legacy.safeTransfer(msg.sender, amount); + } + + function unmigrateTo(address recipient, uint256 amount) external onlyUnmigrationUnlocked { + emit Unmigrated(msg.sender, recipient, amount); + + staking.safeTransferFrom(msg.sender, address(this), amount); + legacy.safeTransfer(recipient, amount); + } + + function updateUnmigrationLock(bool unmigrationLocked_) external onlyOwner { + emit UnmigrationLockUpdated(unmigrationLocked_); + unmigrationLocked = unmigrationLocked_; + } +} \ No newline at end of file diff --git a/contracts/common/mixin/Initializable.sol b/contracts/common/mixin/Initializable.sol index d5220357..2775f03b 100644 --- a/contracts/common/mixin/Initializable.sol +++ b/contracts/common/mixin/Initializable.sol @@ -6,7 +6,10 @@ contract Initializable { modifier initializer() { require(!inited, "already inited"); inited = true; - _; } + + function _disableInitializer() internal { + inited = true; + } } diff --git a/contracts/common/tokens/ERC20Permit.sol b/contracts/common/tokens/ERC20Permit.sol new file mode 100644 index 00000000..18a2f4a7 --- /dev/null +++ b/contracts/common/tokens/ERC20Permit.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.5.17; + +import {ECDSA} from "openzeppelin-solidity/contracts/cryptography/ECDSA.sol"; +import {TestToken} from "./TestToken.sol"; +import {EIP712} from "./../misc/EIP712.sol"; +import {IERC20Permit} from "./../misc/IERC20Permit.sol"; + +// only meant for testing, adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.0.2/contracts/token/ERC20/extensions/ERC20Permit.sol +// modifications: +// - check EIP712 +// - replaced custom errors with strings +// - compress v,r,s for ECDSA.recover (redundant work, only meant for testing) + +contract ERC20Permit is TestToken, IERC20Permit, EIP712 { + mapping(address => uint256) private _nonces; + + constructor( + string memory _name, + string memory _symbol, + string memory _version + ) public TestToken(_name, _symbol) EIP712(_name, _version) {} + + bytes32 private constant PERMIT_TYPEHASH = + keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); + + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public { + if (block.timestamp > deadline) { + revert("ERC2612ExpiredSignature"); + } + + bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)); + + bytes32 hash = _hashTypedDataV4(structHash); + + address signer = ECDSA.recover(hash, _compress(v, r, s)); + if (signer != owner) { + revert("ERC2612InvalidSigner"); + } + + _approve(owner, spender, value); + } + + function nonces(address owner) public view returns (uint256) { + return _nonces[owner]; + } + + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view returns (bytes32) { + return _domainSeparatorV4(); + } + + function _useNonce(address owner) private returns (uint256 current) { + current = _nonces[owner]; + _nonces[owner] = current + 1; + } + + function _compress(uint8 v, bytes32 r, bytes32 s) private pure returns (bytes memory) { + bytes memory signature = new bytes(65); + + assembly { + mstore(add(signature, 0x20), r) + mstore(add(signature, 0x40), s) + mstore8(add(signature, 0x60), v) + } + + return signature; + } +} diff --git a/contracts/root/depositManager/DepositManager.sol b/contracts/root/depositManager/DepositManager.sol index 0cf6ac2b..22beaee9 100644 --- a/contracts/root/depositManager/DepositManager.sol +++ b/contracts/root/depositManager/DepositManager.sol @@ -44,8 +44,9 @@ contract DepositManager is DepositManagerStorage, IDepositManager, ERC721Holder } // new: governance function to migrate MATIC to POL - function migrateMatic(uint256 _amount) external onlyGovernance { - _migrateMatic(_amount); + function migrateMatic() external onlyGovernance { + IERC20 matic = IERC20(registry.contractMap(keccak256("matic"))); + _migrateMatic(matic.balanceOf(address(this))); } function _migrateMatic(uint256 _amount) private { diff --git a/contracts/staking/stakeManager/IStakeManager.sol b/contracts/staking/stakeManager/IStakeManager.sol index 867030fc..ccf3f7b2 100644 --- a/contracts/staking/stakeManager/IStakeManager.sol +++ b/contracts/staking/stakeManager/IStakeManager.sol @@ -17,14 +17,28 @@ contract IStakeManager { address delegator ) external returns (bool); + function transferFundsPOL( + uint256 validatorId, + uint256 amount, + address delegator + ) external returns (bool); + function delegationDeposit( uint256 validatorId, uint256 amount, address delegator ) external returns (bool); + function delegationDepositPOL( + uint256 validatorId, + uint256 amount, + address delegator + ) external returns (bool); + function unstake(uint256 validatorId) external; + function unstakePOL(uint256 validatorId) external; + function totalStakedFor(address addr) external view returns (uint256); function stakeFor( @@ -35,6 +49,14 @@ contract IStakeManager { bytes memory signerPubkey ) public; + function stakeForPOL( + address user, + uint256 amount, + uint256 heimdallFee, + bool acceptDelegation, + bytes memory signerPubkey + ) public; + function checkSignatures( uint256 blockInterval, bytes32 voteHash, diff --git a/contracts/staking/stakeManager/StakeManager.sol b/contracts/staking/stakeManager/StakeManager.sol index 6feca104..e1e70cb6 100644 --- a/contracts/staking/stakeManager/StakeManager.sol +++ b/contracts/staking/stakeManager/StakeManager.sol @@ -22,6 +22,7 @@ import {StakeManagerStorageExtension} from "./StakeManagerStorageExtension.sol"; import {IGovernance} from "../../common/governance/IGovernance.sol"; import {Initializable} from "../../common/mixin/Initializable.sol"; import {StakeManagerExtension} from "./StakeManagerExtension.sol"; +import {IPolygonMigration} from "../../common/misc/IPolygonMigration.sol"; contract StakeManager is StakeManagerStorage, @@ -67,7 +68,9 @@ contract StakeManager is require(validators[validatorId].contractAddress == msg.sender, "Invalid contract address"); } - constructor() public GovernanceLockable(address(0x0)) initializer {} + constructor() public GovernanceLockable(address(0x0)) { + _disableInitializer(); + } function initialize( address _registry, @@ -107,6 +110,30 @@ contract StakeManager is delegationEnabled = true; } + function reinitialize( + address _NFTContract, + address _stakingLogger, + address _validatorShareFactory, + address _extensionCode + ) external onlyGovernance { + require(isContract(_extensionCode)); + eventsHub = address(0x0); + extensionCode = _extensionCode; + NFTContract = StakingNFT(_NFTContract); + logger = StakingInfo(_stakingLogger); + validatorShareFactory = ValidatorShareFactory(_validatorShareFactory); + } + + function initializePOL( + address _tokenNew, + address _migration + ) external onlyGovernance { + tokenMatic = IERC20(token); + token = IERC20(_tokenNew); + migration = IPolygonMigration(_migration); + _convertMaticToPOL(tokenMatic.balanceOf(address(this))); + } + function isOwner() public view returns (bool) { address _owner; bytes32 position = keccak256("matic.network.proxy.owner"); @@ -199,7 +226,11 @@ contract StakeManager is // Housekeeping function. @todo remove later function forceUnstake(uint256 validatorId) external onlyGovernance { - _unstake(validatorId, currentEpoch); + _unstake(validatorId, currentEpoch, false); + } + + function forceUnstakePOL(uint256 validatorId) external onlyGovernance { + _unstake(validatorId, currentEpoch, true); } function setCurrentEpoch(uint256 _currentEpoch) external onlyGovernance { @@ -212,7 +243,7 @@ contract StakeManager is } /** - @dev Change the number of validators required to allow a passed header root + * @dev Change the number of validators required to allow a passed header root */ function updateValidatorThreshold(uint256 newThreshold) public onlyGovernance { require(newThreshold != 0); @@ -313,45 +344,23 @@ contract StakeManager is } function drain(address destination, uint256 amount) external onlyGovernance { - _transferToken(destination, amount); - } - - function reinitialize( - address _NFTContract, - address _stakingLogger, - address _validatorShareFactory, - address _extensionCode - ) external onlyGovernance { - require(isContract(_extensionCode)); - eventsHub = address(0x0); - extensionCode = _extensionCode; - NFTContract = StakingNFT(_NFTContract); - logger = StakingInfo(_stakingLogger); - validatorShareFactory = ValidatorShareFactory(_validatorShareFactory); + _transferToken(destination, amount, true); } /** Public Methods */ - function topUpForFee(address user, uint256 heimdallFee) public onlyWhenUnlocked { - _transferAndTopUp(user, msg.sender, heimdallFee, 0); + _transferAndTopUp(user, msg.sender, heimdallFee, 0, true); } - function claimFee( - uint256 accumFeeAmount, - uint256 index, - bytes memory proof - ) public { - //Ignoring other params because rewards' distribution is on chain - require( - keccak256(abi.encode(msg.sender, accumFeeAmount)).checkMembership(index, accountStateRoot, proof), - "Wrong acc proof" - ); + function claimFee(uint256 accumFeeAmount, uint256 index, bytes memory proof) public { + require(keccak256(abi.encode(msg.sender, accumFeeAmount)).checkMembership(index, accountStateRoot, proof), "Wrong acc proof"); uint256 withdrawAmount = accumFeeAmount.sub(userFeeExit[msg.sender]); - _claimFee(msg.sender, withdrawAmount); + totalHeimdallFee = totalHeimdallFee.sub(withdrawAmount); + logger.logClaimFee(msg.sender, withdrawAmount); userFeeExit[msg.sender] = accumFeeAmount; - _transferToken(msg.sender, withdrawAmount); + _transferToken(msg.sender, withdrawAmount, true); } function totalStakedFor(address user) external view returns (uint256) { @@ -404,14 +413,22 @@ contract StakeManager is ) external { require(msg.sender == address(this), "not allowed"); // dethrone - _transferAndTopUp(auctionUser, auctionUser, heimdallFee, 0); - _unstake(validatorId, currentEpoch); + _transferAndTopUp(auctionUser, auctionUser, heimdallFee, 0, true); + _unstake(validatorId, currentEpoch, true); uint256 newValidatorId = _stakeFor(auctionUser, auctionAmount, acceptDelegation, signerPubkey); logger.logConfirmAuction(newValidatorId, validatorId, auctionAmount); } function unstake(uint256 validatorId) external onlyStaker(validatorId) { + _unstakeValidator(validatorId, false); + } + + function unstakePOL(uint256 validatorId) external onlyStaker(validatorId) { + _unstakeValidator(validatorId, true); + } + + function _unstakeValidator(uint256 validatorId, bool pol) internal { require(validatorAuction[validatorId].amount == 0); Status status = validators[validatorId].status; @@ -422,44 +439,63 @@ contract StakeManager is ); uint256 exitEpoch = currentEpoch.add(1); // notice period - _unstake(validatorId, exitEpoch); + _unstake(validatorId, exitEpoch, pol); } - function transferFunds( - uint256 validatorId, - uint256 amount, - address delegator - ) external returns (bool) { - require( - validators[validatorId].contractAddress == msg.sender || - Registry(registry).getSlashingManagerAddress() == msg.sender, - "not allowed" - ); - return token.transfer(delegator, amount); + function transferFunds(uint256 validatorId, uint256 amount, address delegator) external returns (bool) { + return _transferFunds(validatorId, amount, delegator, false); } - function delegationDeposit( - uint256 validatorId, - uint256 amount, - address delegator - ) external onlyDelegation(validatorId) returns (bool) { - return token.transferFrom(delegator, address(this), amount); + function transferFundsPOL(uint256 validatorId, uint256 amount, address delegator) external returns (bool) { + return _transferFunds(validatorId, amount, delegator, true); } - function stakeFor( - address user, - uint256 amount, - uint256 heimdallFee, - bool acceptDelegation, - bytes memory signerPubkey - ) public onlyWhenUnlocked { + function _transferFunds(uint256 validatorId, uint256 amount, address delegator, bool pol) internal returns (bool) { + require(validators[validatorId].contractAddress == msg.sender || Registry(registry).getSlashingManagerAddress() == msg.sender, "not allowed"); + if (!pol) _convertPOLToMatic(amount); + IERC20 token_ = _getToken(pol); + return token_.transfer(delegator, amount); + } + + function delegationDeposit(uint256 validatorId, uint256 amount, address delegator) external onlyDelegation(validatorId) returns (bool) { + return _delegationDeposit(amount, delegator, false); + } + + function delegationDepositPOL(uint256 validatorId, uint256 amount, address delegator) external onlyDelegation(validatorId) returns (bool) { + return _delegationDeposit(amount, delegator, true); + } + + function _delegationDeposit(uint256 amount, address delegator, bool pol) internal returns (bool) { + IERC20 token_ = _getToken(pol); + bool result = token_.transferFrom(delegator, address(this), amount); + if (!pol) _convertMaticToPOL(amount); + return result; + } + + function stakeFor(address user, uint256 amount, uint256 heimdallFee, bool acceptDelegation, bytes memory signerPubkey) public onlyWhenUnlocked { + _stakeFor(user, amount, heimdallFee, acceptDelegation, signerPubkey, false); + } + + function stakeForPOL(address user, uint256 amount, uint256 heimdallFee, bool acceptDelegation, bytes memory signerPubkey) public onlyWhenUnlocked { + _stakeFor(user, amount, heimdallFee, acceptDelegation, signerPubkey, true); + } + + function _stakeFor(address user, uint256 amount, uint256 heimdallFee, bool acceptDelegation, bytes memory signerPubkey, bool pol) internal { require(currentValidatorSetSize() < validatorThreshold, "no more slots"); require(amount >= minDeposit, "not enough deposit"); - _transferAndTopUp(user, msg.sender, heimdallFee, amount); + _transferAndTopUp(user, msg.sender, heimdallFee, amount, pol); _stakeFor(user, amount, acceptDelegation, signerPubkey); } function unstakeClaim(uint256 validatorId) public onlyStaker(validatorId) { + _unstakeClaim(validatorId, false); + } + + function unstakeClaimPOL(uint256 validatorId) public onlyStaker(validatorId) { + _unstakeClaim(validatorId, true); + } + + function _unstakeClaim(uint256 validatorId, bool pol) internal { uint256 deactivationEpoch = validators[validatorId].deactivationEpoch; // can only claim stake back after WITHDRAWAL_DELAY require( @@ -473,7 +509,7 @@ contract StakeManager is totalStaked = newTotalStaked; // claim last checkpoint reward if it was signed by validator - _liquidateRewards(validatorId, msg.sender); + _liquidateRewards(validatorId, msg.sender, pol); NFTContract.burn(validatorId); @@ -484,19 +520,23 @@ contract StakeManager is signerToValidator[validators[validatorId].signer] = INCORRECT_VALIDATOR_ID; validators[validatorId].status = Status.Unstaked; - _transferToken(msg.sender, amount); + _transferToken(msg.sender, amount, pol); logger.logUnstaked(msg.sender, validatorId, amount, newTotalStaked); } - function restake( - uint256 validatorId, - uint256 amount, - bool stakeRewards - ) public onlyWhenUnlocked onlyStaker(validatorId) { + function restake(uint256 validatorId, uint256 amount, bool stakeRewards) public onlyWhenUnlocked onlyStaker(validatorId) { + _restake(validatorId, amount, stakeRewards, false); + } + + function restakePOL(uint256 validatorId, uint256 amount, bool stakeRewards) public onlyWhenUnlocked onlyStaker(validatorId) { + _restake(validatorId, amount, stakeRewards, true); + } + + function _restake(uint256 validatorId, uint256 amount, bool stakeRewards, bool pol) internal { require(validators[validatorId].deactivationEpoch == 0, "No restaking"); if (amount > 0) { - _transferTokenFrom(msg.sender, address(this), amount); + _transferTokenFrom(msg.sender, address(this), amount, pol); } _updateRewards(validatorId); @@ -517,8 +557,16 @@ contract StakeManager is } function withdrawRewards(uint256 validatorId) public onlyStaker(validatorId) { + _withdrawRewards(validatorId, false); + } + + function withdrawRewardsPOL(uint256 validatorId) public onlyStaker(validatorId) { + _withdrawRewards(validatorId, true); + } + + function _withdrawRewards(uint256 validatorId, bool pol) internal { _updateRewards(validatorId); - _liquidateRewards(validatorId, msg.sender); + _liquidateRewards(validatorId, msg.sender, pol); } function migrateDelegation( @@ -683,49 +731,7 @@ contract StakeManager is } function slash(bytes calldata _slashingInfoList) external returns (uint256) { - require(Registry(registry).getSlashingManagerAddress() == msg.sender, "Not slash manager"); - - RLPReader.RLPItem[] memory slashingInfoList = _slashingInfoList.toRlpItem().toList(); - int256 valJailed; - uint256 jailedAmount; - uint256 totalAmount; - uint256 i; - - for (; i < slashingInfoList.length; i++) { - RLPReader.RLPItem[] memory slashData = slashingInfoList[i].toList(); - - uint256 validatorId = slashData[0].toUint(); - _updateRewards(validatorId); - - uint256 _amount = slashData[1].toUint(); - totalAmount = totalAmount.add(_amount); - - address delegationContract = validators[validatorId].contractAddress; - if (delegationContract != address(0x0)) { - uint256 delSlashedAmount = - IValidatorShare(delegationContract).slash( - validators[validatorId].amount, - validators[validatorId].delegatedAmount, - _amount - ); - _amount = _amount.sub(delSlashedAmount); - } - - uint256 validatorStakeSlashed = validators[validatorId].amount.sub(_amount); - validators[validatorId].amount = validatorStakeSlashed; - - if (validatorStakeSlashed == 0) { - _unstake(validatorId, currentEpoch); - } else if (slashData[2].toBoolean()) { - jailedAmount = jailedAmount.add(_jail(validatorId, 1)); - valJailed++; - } - } - - //update timeline - updateTimeline(-int256(totalAmount.add(jailedAmount)), -valJailed, 0); - - return totalAmount; + revert(); } function unjail(uint256 validatorId) public onlyStaker(validatorId) { @@ -945,7 +951,7 @@ contract StakeManager is uint256 initialRewardPerStake = validators[validatorId].initialRewardPerStake; - // attempt to save gas in case if rewards were updated previosuly + // attempt to save gas in case if rewards were updated previously if (initialRewardPerStake < currentRewardPerStake) { uint256 validatorsStake = validators[validatorId].amount; uint256 delegatedAmount = validators[validatorId].delegatedAmount; @@ -1060,19 +1066,6 @@ contract StakeManager is ); } - function _jail(uint256 validatorId, uint256 jailCheckpoints) internal returns (uint256) { - address delegationContract = validators[validatorId].contractAddress; - if (delegationContract != address(0x0)) { - IValidatorShare(delegationContract).lock(); - } - - uint256 _currentEpoch = currentEpoch; - validators[validatorId].jailTime = _currentEpoch.add(jailCheckpoints); - validators[validatorId].status = Status.Locked; - logger.logJailed(validatorId, _currentEpoch, validators[validatorId].signer); - return validators[validatorId].amount.add(validators[validatorId].delegatedAmount); - } - function _stakeFor( address user, uint256 amount, @@ -1120,9 +1113,9 @@ contract StakeManager is return validatorId; } - function _unstake(uint256 validatorId, uint256 exitEpoch) internal { - // TODO: if validators unstake and slashed to 0, he will be forced to unstake again - // must think how to handle it correctly + function _unstake(uint256 validatorId, uint256 exitEpoch, bool pol) internal { + require(validators[validatorId].deactivationEpoch == 0); + _updateRewards(validatorId); uint256 amount = validators[validatorId].amount; @@ -1139,7 +1132,7 @@ contract StakeManager is } _removeSigner(validators[validatorId].signer); - _liquidateRewards(validatorId, validator); + _liquidateRewards(validatorId, validator, pol); uint256 targetEpoch = exitEpoch <= currentEpoch ? 0 : exitEpoch; updateTimeline(-(int256(amount) + delegationAmount), -1, targetEpoch); @@ -1159,48 +1152,39 @@ contract StakeManager is currentEpoch = nextEpoch; } - function _liquidateRewards(uint256 validatorId, address validatorUser) private { + function _liquidateRewards(uint256 validatorId, address validatorUser, bool pol) private { uint256 reward = validators[validatorId].reward.sub(INITIALIZED_AMOUNT); totalRewardsLiquidated = totalRewardsLiquidated.add(reward); validators[validatorId].reward = INITIALIZED_AMOUNT; - _transferToken(validatorUser, reward); + _transferToken(validatorUser, reward, pol); logger.logClaimRewards(validatorId, reward, totalRewardsLiquidated); } - function _transferToken(address destination, uint256 amount) private { - require(token.transfer(destination, amount), "transfer failed"); + function _transferToken(address destination, uint256 amount, bool pol) private { + if (!pol) _convertPOLToMatic(amount); + IERC20 token_ = _getToken(pol); + require(token_.transfer(destination, amount), "transfer failed"); } - function _transferTokenFrom( - address from, - address destination, - uint256 amount - ) private { - require(token.transferFrom(from, destination, amount), "transfer from failed"); + // Do not use this function to transfer from self. + function _transferTokenFrom(address from, address destination, uint256 amount, bool pol) private { + IERC20 token_ = _getToken(pol); + require(token_.transferFrom(from, destination, amount), "transfer from failed"); + if (!pol && destination == address(this)) _convertMaticToPOL(amount); } - function _transferAndTopUp( - address user, - address from, - uint256 fee, - uint256 additionalAmount - ) private { + function _transferAndTopUp(address user, address from, uint256 fee, uint256 additionalAmount, bool pol) private { require(fee >= minHeimdallFee, "fee too small"); - _transferTokenFrom(from, address(this), fee.add(additionalAmount)); + _transferTokenFrom(from, address(this), fee.add(additionalAmount), pol); totalHeimdallFee = totalHeimdallFee.add(fee); logger.logTopUpFee(user, fee); } - function _claimFee(address user, uint256 amount) private { - totalHeimdallFee = totalHeimdallFee.sub(amount); - logger.logClaimFee(user, amount); - } - function _insertSigner(address newSigner) internal { signers.push(newSigner); - uint lastIndex = signers.length - 1; - uint i = lastIndex; + uint256 lastIndex = signers.length - 1; + uint256 i = lastIndex; for (; i > 0; --i) { address signer = signers[i - 1]; if (signer < newSigner) { @@ -1230,4 +1214,24 @@ contract StakeManager is signers.length = totalSigners - 1; } + + function convertMaticToPOL(uint256 amount) external onlyGovernance { + _convertMaticToPOL(amount); + } + + function _convertMaticToPOL(uint256 amount) internal { + require(tokenMatic.balanceOf(address(this)) >= amount, "Lacking MATIC"); + tokenMatic.approve(address(migration), amount); + migration.migrate(amount); + } + + function _convertPOLToMatic(uint256 amount) internal { + require(token.balanceOf(address(this)) >= amount, "Lacking POL"); + token.approve(address(migration), amount); + migration.unmigrate(amount); + } + + function _getToken(bool pol) internal view returns (IERC20 token_) { + token_ = pol ? token : tokenMatic; + } } diff --git a/contracts/staking/stakeManager/StakeManagerStorageExtension.sol b/contracts/staking/stakeManager/StakeManagerStorageExtension.sol index dca5cd23..adf715f8 100644 --- a/contracts/staking/stakeManager/StakeManagerStorageExtension.sol +++ b/contracts/staking/stakeManager/StakeManagerStorageExtension.sol @@ -1,5 +1,8 @@ pragma solidity 0.5.17; +import {IPolygonMigration} from "../../common/misc/IPolygonMigration.sol"; +import {IERC20} from "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; + contract StakeManagerStorageExtension { address public eventsHub; uint256 public rewardPerStake; @@ -14,4 +17,7 @@ contract StakeManagerStorageExtension { uint256 public maxRewardedCheckpoints; // increase / decrease value for faster or slower checkpoints, 0 - 100% uint256 public checkpointRewardDelta; + + IERC20 public tokenMatic; + IPolygonMigration public migration; } diff --git a/contracts/staking/validatorShare/ValidatorShare.sol b/contracts/staking/validatorShare/ValidatorShare.sol index 68cdea8e..52116445 100644 --- a/contracts/staking/validatorShare/ValidatorShare.sol +++ b/contracts/staking/validatorShare/ValidatorShare.sol @@ -9,6 +9,7 @@ import {OwnableLockable} from "../../common/mixin/OwnableLockable.sol"; import {IStakeManager} from "../stakeManager/IStakeManager.sol"; import {IValidatorShare} from "./IValidatorShare.sol"; import {Initializable} from "../../common/mixin/Initializable.sol"; +import {IERC20Permit} from "./../../common/misc/IERC20Permit.sol"; contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, Initializable { struct DelegatorUnbond { @@ -17,7 +18,7 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I } uint256 constant EXCHANGE_RATE_PRECISION = 100; - // maximum matic possible, even if rate will be 1 and all matic will be staken in one go, it will result in 10 ^ 58 shares + // maximum matic possible, even if rate will be 1 and all matic will be staked in one go, it will result in 10 ^ 58 shares uint256 constant EXCHANGE_RATE_HIGH_PRECISION = 10**29; uint256 constant MAX_COMMISION_RATE = 100; uint256 constant REWARD_PRECISION = 10**25; @@ -48,6 +49,12 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I EventsHub public eventsHub; + IERC20Permit public polToken; + + constructor() public { + _disableInitializer(); + } + // onlyOwner will prevent this contract from initializing, since it's owner is going to be 0x0 address function initialize( uint256 _validatorId, @@ -108,17 +115,54 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I /** Public Methods */ + function buyVoucher(uint256 _amount, uint256 _minSharesToMint) public returns (uint256 amountToDeposit) { + return _buyVoucher(_amount, _minSharesToMint, false); + } + + // @dev permit only available on pol token + // @dev txn fails if frontrun, use buyVoucher instead + function buyVoucherWithPermit( + uint256 _amount, + uint256 _minSharesToMint, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public returns (uint256 amountToDeposit) { + IERC20Permit _polToken = _getOrCachePOLToken(); + uint256 nonceBefore = _polToken.nonces(msg.sender); + _polToken.permit(msg.sender, address(stakeManager), _amount, deadline, v, r, s); + require(_polToken.nonces(msg.sender) == nonceBefore + 1, "Invalid permit"); + return _buyVoucher(_amount, _minSharesToMint, true); // invokes stakeManager to pull token from msg.sender + } + + function buyVoucherPOL(uint256 _amount, uint256 _minSharesToMint) public returns (uint256 amountToDeposit) { + return _buyVoucher(_amount, _minSharesToMint, true); + } + + function _buyVoucher(uint256 _amount, uint256 _minSharesToMint, bool pol) internal returns (uint256 amountToDeposit) { + _withdrawAndTransferReward(msg.sender, pol); - function buyVoucher(uint256 _amount, uint256 _minSharesToMint) public returns(uint256 amountToDeposit) { - _withdrawAndTransferReward(msg.sender); - amountToDeposit = _buyShares(_amount, _minSharesToMint, msg.sender); - require(stakeManager.delegationDeposit(validatorId, amountToDeposit, msg.sender), "deposit failed"); - + require( + pol + ? stakeManager.delegationDepositPOL(validatorId, amountToDeposit, msg.sender) + : stakeManager.delegationDeposit(validatorId, amountToDeposit, msg.sender), + "deposit failed" + ); + return amountToDeposit; } - function restake() public returns(uint256, uint256) { + function restake() public returns (uint256, uint256) { + return _restake(false); + } + + function restakePOL() public returns (uint256, uint256) { + return _restake(true); + } + + function _restake(bool pol) public returns (uint256, uint256) { address user = msg.sender; uint256 liquidReward = _withdrawReward(user); uint256 amountRestaked; @@ -131,7 +175,9 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I if (liquidReward > amountRestaked) { // return change to the user require( - stakeManager.transferFunds(validatorId, liquidReward - amountRestaked, user), + pol + ? stakeManager.transferFundsPOL(validatorId, liquidReward - amountRestaked, user) + : stakeManager.transferFunds(validatorId, liquidReward - amountRestaked, user), "Insufficent rewards" ); stakingLogger.logDelegatorClaimRewards(validatorId, user, liquidReward - amountRestaked); @@ -145,11 +191,19 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I } function sellVoucher(uint256 claimAmount, uint256 maximumSharesToBurn) public { - (uint256 shares, uint256 _withdrawPoolShare) = _sellVoucher(claimAmount, maximumSharesToBurn); + __sellVoucher(claimAmount, maximumSharesToBurn, false); + } + + function sellVoucherPOL(uint256 claimAmount, uint256 maximumSharesToBurn) public { + __sellVoucher(claimAmount, maximumSharesToBurn, true); + } + + function __sellVoucher(uint256 claimAmount, uint256 maximumSharesToBurn, bool pol) internal { + (uint256 shares, uint256 _withdrawPoolShare) = _sellVoucher(claimAmount, maximumSharesToBurn, pol); DelegatorUnbond memory unbond = unbonds[msg.sender]; unbond.shares = unbond.shares.add(_withdrawPoolShare); - // refresh undond period + // refresh unbond period unbond.withdrawEpoch = stakeManager.epoch(); unbonds[msg.sender] = unbond; @@ -159,12 +213,20 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I } function withdrawRewards() public { - uint256 rewards = _withdrawAndTransferReward(msg.sender); + _withdrawRewards(false); + } + + function withdrawRewardsPOL() public { + _withdrawRewards(true); + } + + function _withdrawRewards(bool pol) internal { + uint256 rewards = _withdrawAndTransferReward(msg.sender, pol); require(rewards >= minAmount, "Too small rewards amount"); } function migrateOut(address user, uint256 amount) external onlyOwner { - _withdrawAndTransferReward(user); + _withdrawAndTransferReward(user, true); (uint256 totalStaked, uint256 rate) = getTotalStake(user); require(totalStaked >= amount, "Migrating too much"); @@ -181,13 +243,21 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I } function migrateIn(address user, uint256 amount) external onlyOwner { - _withdrawAndTransferReward(user); + _withdrawAndTransferReward(user, true); _buyShares(amount, 0, user); - } + } function unstakeClaimTokens() public { + _unstakeClaimTokens(false); + } + + function unstakeClaimTokensPOL() public { + _unstakeClaimTokens(true); + } + + function _unstakeClaimTokens(bool pol) internal { DelegatorUnbond memory unbond = unbonds[msg.sender]; - uint256 amount = _unstakeClaimTokens(unbond); + uint256 amount = _unstakeClaimTokens(unbond, pol); delete unbonds[msg.sender]; stakingLogger.logDelegatorUnstaked(validatorId, msg.sender, amount); } @@ -197,22 +267,7 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I uint256 delegatedAmount, uint256 totalAmountToSlash ) external onlyOwner returns (uint256) { - uint256 _withdrawPool = withdrawPool; - uint256 delegationAmount = delegatedAmount.add(_withdrawPool); - if (delegationAmount == 0) { - return 0; - } - // total amount to be slashed from delegation pool (active + inactive) - uint256 _amountToSlash = delegationAmount.mul(totalAmountToSlash).div(validatorStake.add(delegationAmount)); - uint256 _amountToSlashWithdrawalPool = _withdrawPool.mul(_amountToSlash).div(delegationAmount); - - // slash inactive pool - uint256 stakeSlashed = _amountToSlash.sub(_amountToSlashWithdrawalPool); - stakeManager.decreaseValidatorDelegatedAmount(validatorId, stakeSlashed); - activeAmount = activeAmount.sub(stakeSlashed); - - withdrawPool = withdrawPool.sub(_amountToSlashWithdrawalPool); - return _amountToSlash; + revert("Slashing disabled"); } function updateDelegation(bool _delegation) external onlyOwner { @@ -234,9 +289,16 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I /** New shares exit API */ - function sellVoucher_new(uint256 claimAmount, uint256 maximumSharesToBurn) public { - (uint256 shares, uint256 _withdrawPoolShare) = _sellVoucher(claimAmount, maximumSharesToBurn); + _sellVoucher_new(claimAmount, maximumSharesToBurn, false); + } + + function sellVoucher_newPOL(uint256 claimAmount, uint256 maximumSharesToBurn) public { + _sellVoucher_new(claimAmount, maximumSharesToBurn, true); + } + + function _sellVoucher_new(uint256 claimAmount, uint256 maximumSharesToBurn, bool pol) public { + (uint256 shares, uint256 _withdrawPoolShare) = _sellVoucher(claimAmount, maximumSharesToBurn, pol); uint256 unbondNonce = unbondNonces[msg.sender].add(1); @@ -252,8 +314,16 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I } function unstakeClaimTokens_new(uint256 unbondNonce) public { + _unstakeClaimTokens_new(unbondNonce, false); + } + + function unstakeClaimTokens_newPOL(uint256 unbondNonce) public { + _unstakeClaimTokens_new(unbondNonce, true); + } + + function _unstakeClaimTokens_new(uint256 unbondNonce, bool pol) internal { DelegatorUnbond memory unbond = unbonds_new[msg.sender][unbondNonce]; - uint256 amount = _unstakeClaimTokens(unbond); + uint256 amount = _unstakeClaimTokens(unbond, pol); delete unbonds_new[msg.sender][unbondNonce]; _getOrCacheEventsHub().logDelegatorUnstakedWithId(validatorId, msg.sender, amount, unbondNonce); } @@ -271,7 +341,21 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I return _eventsHub; } - function _sellVoucher(uint256 claimAmount, uint256 maximumSharesToBurn) private returns(uint256, uint256) { + function _getOrCachePOLToken() private returns (IERC20Permit) { + IERC20Permit _polToken = polToken; + if (_polToken == IERC20Permit(0x0)) { + _polToken = IERC20Permit(Registry(stakeManager.getRegistry()).contractMap(keccak256("pol"))); + require(_polToken != IERC20Permit(0x0), "unset"); + polToken = _polToken; + } + return _polToken; + } + + function _sellVoucher( + uint256 claimAmount, + uint256 maximumSharesToBurn, + bool pol + ) private returns (uint256, uint256) { // first get how much staked in total and compare to target unstake amount (uint256 totalStaked, uint256 rate) = getTotalStake(msg.sender); require(totalStaked != 0 && totalStaked >= claimAmount, "Too much requested"); @@ -281,7 +365,7 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I uint256 shares = claimAmount.mul(precision).div(rate); require(shares <= maximumSharesToBurn, "too much slippage"); - _withdrawAndTransferReward(msg.sender); + _withdrawAndTransferReward(msg.sender, pol); _burn(msg.sender, shares); stakeManager.updateValidatorState(validatorId, -int256(claimAmount)); @@ -294,7 +378,7 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I return (shares, _withdrawPoolShare); } - function _unstakeClaimTokens(DelegatorUnbond memory unbond) private returns(uint256) { + function _unstakeClaimTokens(DelegatorUnbond memory unbond, bool pol) private returns (uint256) { uint256 shares = unbond.shares; require( unbond.withdrawEpoch.add(stakeManager.withdrawalDelay()) <= stakeManager.epoch() && shares > 0, @@ -305,7 +389,10 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I withdrawShares = withdrawShares.sub(shares); withdrawPool = withdrawPool.sub(_amount); - require(stakeManager.transferFunds(validatorId, _amount, msg.sender), "Insufficent rewards"); + require( + pol ? stakeManager.transferFundsPOL(validatorId, _amount, msg.sender) : stakeManager.transferFunds(validatorId, _amount, msg.sender), + "Insufficent rewards" + ); return _amount; } @@ -358,10 +445,13 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I return liquidRewards; } - function _withdrawAndTransferReward(address user) private returns (uint256) { + function _withdrawAndTransferReward(address user, bool pol) private returns (uint256) { uint256 liquidRewards = _withdrawReward(user); if (liquidRewards != 0) { - require(stakeManager.transferFunds(validatorId, liquidRewards, user), "Insufficent rewards"); + require( + pol ? stakeManager.transferFundsPOL(validatorId, liquidRewards, user) : stakeManager.transferFunds(validatorId, liquidRewards, user), + "Insufficent rewards" + ); stakingLogger.logDelegatorClaimRewards(validatorId, user, liquidRewards); } return liquidRewards; @@ -395,15 +485,22 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I return _amount; } - function _transfer( - address from, - address to, - uint256 value - ) internal { - // get rewards for recipient - _withdrawAndTransferReward(to); + function transferPOL(address to, uint256 value) public returns (bool) { + _transfer(to, value, true); + return true; + } + + function transfer(address to, uint256 value) public returns (bool) { + _transfer(to, value, false); + return true; + } + + function _transfer(address to, uint256 value, bool pol) internal { + address from = msg.sender; + // get rewards for recipient + _withdrawAndTransferReward(to, pol); // convert rewards to shares - _withdrawAndTransferReward(from); + _withdrawAndTransferReward(from, pol); // move shares to recipient super._transfer(from, to, value); _getOrCacheEventsHub().logSharesTransfer(validatorId, from, to, value); diff --git a/foundry.toml b/foundry.toml index e0a6f2d5..47ef9c57 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,20 +1,25 @@ [profile.default] src = 'contracts' out = 'out' -libs = ['node_modules'] -match-path = 'test/foundry' +libs = ['node_modules', 'lib'] +no-match-contract = 'StakeManagerTestable' optimizer = true optimizer_runs = 200 via_ir = true cache_path = 'forge-cache' verbosity = 2 -solc_version = '0.5.17' -ffi = true +ffi = true remappings = [ "openzeppelin-solidity/=node_modules/openzeppelin-solidity/", - "solidity-rlp/=node_modules/solidity-rlp/" + "solidity-rlp/=node_modules/solidity-rlp/", + "@ensdomains/=node_modules/@ensdomains/", + "eth-gas-reporter/=node_modules/eth-gas-reporter/", + "forge-std/=lib/forge-std/src/", + "hardhat/=node_modules/hardhat/" ] +fs_permissions = [{ access = "read", path = "./scripts/"}, { access = "read", path = "./test/"}, { access = "read", path = "./out/"}] +auto_detect_solc = true [invariant] fail_on_revert = false @@ -33,11 +38,11 @@ number_underscore = "thousands" [rpc_endpoints] anvil = "http://127.0.0.1:8545" -mainnet = "https://mainnet.infura.io/v3/${INFURA_KEY}" -goerli = "https://goerli.infura.io/v3/${INFURA_KEY}" -sepolia = "https://sepolia.infura.io/v3/${INFURA_KEY}" -polygon_pos = "https://polygon-mainnet.infura.io/v3/${INFURA_KEY}" -mumbai = "https://polygon-mumbai.infura.io/v3/${INFURA_KEY}" +mainnet = "https://mainnet.infura.io/v3/${INFURA_TOKEN}" +goerli = "https://goerli.infura.io/v3/${INFURA_TOKEN}" +sepolia = "https://sepolia.infura.io/v3/${INFURA_TOKEN}" +polygon_pos = "https://polygon-mainnet.infura.io/v3/${INFURA_TOKEN}" +mumbai = "https://polygon-mumbai.infura.io/v3/${INFURA_TOKEN}" polygon_zkevm = "https://zkevm-rpc.com" polygon_zkevm_testnet = "https://rpc.public.zkevm-test.net" diff --git a/hardhat.config.cjs b/hardhat.config.cjs index ae9d72bd..beab3c53 100644 --- a/hardhat.config.cjs +++ b/hardhat.config.cjs @@ -25,13 +25,36 @@ const baseConfig = { }, }, solidity: { - version: foundryConfig.profile.default.solc_version, - settings: { - optimizer: { - enabled: true, - runs: foundryConfig.profile.default.optimizer_runs, + compilers: [ + { + version: "0.5.4", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, }, - }, + { + version: "0.5.17", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + { + version: "0.8.4", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + ], + }, paths: { sources: "./contracts", diff --git a/lib/forge-std b/lib/forge-std new file mode 160000 index 00000000..07263d19 --- /dev/null +++ b/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 07263d193d621c4b2b0ce8b4d54af58f6957d97d diff --git a/package-lock.json b/package-lock.json index 24eefeb1..10dd3475 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "mocha": "10.2.0", "nunjucks": "^3.2.0", "openzeppelin-solidity": "2.2.0", - "solidity-rlp": "^2.0.0" + "solidity-rlp": "2.0.8" }, "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^2.0.0", diff --git a/package.json b/package.json index 731f10c9..8c952be0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "matic-protocol", "description": "New repo using Hardhat and Foundry", - "version": "0.3.2", + "version": "0.4.0", "main": "hardhat.config.cjs", "type": "module", "directories": { @@ -11,6 +11,7 @@ "scripts": { "build": "forge build", "test:hardhat": "npx hardhat test", + "generate:interfaces": "scripts/helpers/generateInterfaces.sh", "test:foundry": "forge test", "test:ci": "scripts/run-test.sh", "testrpc": "ganache --chain.hardfork istanbul --wallet.mnemonic 'clock radar mass judge dismiss just intact mind resemble fringe diary casino' -p 8545 --gasLimit 10000000 --gasPrice 0 --chain.allowUnlimitedContractSize --accounts 200", @@ -18,7 +19,10 @@ "coverage": "LOCAL_NETWORK=true npx hardhat coverage --solcoverjs .solcover.cjs", "bor:simulate": "cd test-bor-docker && bash run-docker.sh", "bor:stop": "cd test-bor-docker && bash stop-docker.sh", - "bor:clean": "cd test-bor-docker && bash clean.sh" + "bor:clean": "cd test-bor-docker && bash clean.sh", + "upgrade:stakeManager:sepolia": "forge script scripts/deployers/pol-upgrade/UpgradeStakeManager_Sepolia.s.sol --rpc-url sepolia", + "upgrade:depositManager:sepolia": "forge script scripts/deployers/pol-upgrade/UpgradeDepositManager_Sepolia.s.sol --rpc-url sepolia", + "upgrade:pol:mainnet": "forge script scripts/deployers/pol-upgrade/UpgradeStake_DepositManager_Mainnet.s.sol --rpc-url mainnet" }, "keywords": [], "author": "Jaynti Kanani , Simon Dosch scripts/helpers/interfaces/$contract.generated.sol +} + +forge compile --skip test script + +mkdir -p scripts/helpers/interfaces + +find out -type f -print0 | while read -d $'\0' file +do + echo $file + create_interface +done + + diff --git a/test/foundry/ForkupgradeStakeManager.t.sol b/test/foundry/ForkupgradeStakeManager.t.sol new file mode 100644 index 00000000..0c6d12b9 --- /dev/null +++ b/test/foundry/ForkupgradeStakeManager.t.sol @@ -0,0 +1,88 @@ +pragma solidity ^0.8.4; + +// These imports get generated by npm run generate:interfaces +import {StakeManager} from "../../scripts/helpers/interfaces/StakeManager.generated.sol"; +import {StakeManagerProxy} from "../../scripts/helpers/interfaces/StakeManagerProxy.generated.sol"; +import {ValidatorShare} from "../../scripts/helpers/interfaces/ValidatorShare.generated.sol"; +import {DepositManager} from "../../scripts/helpers/interfaces/DepositManager.generated.sol"; +import {Registry} from "../../scripts/helpers/interfaces/Registry.generated.sol"; +import {ERC20} from "../../scripts/helpers/interfaces/ERC20.generated.sol"; +import {Proxy} from "../../scripts/helpers/interfaces/Proxy.generated.sol"; + +import {UpgradeStake_DepositManager_Mainnet} from "../../scripts/deployers/pol-upgrade/UpgradeStake_DepositManager_Mainnet.s.sol"; +import {Timelock} from "../../contracts/common/misc/ITimelock.sol"; + +import "forge-std/Test.sol"; + +contract ForkupgradeStakeManagerTest is Test, UpgradeStake_DepositManager_Mainnet { + uint256 mainnetFork; + + function setUp() public { + mainnetFork = vm.createFork(vm.rpcUrl("mainnet")); + vm.selectFork(mainnetFork); + } + + function test_UpgradeStakeManager() public { + assertEq(vm.activeFork(), mainnetFork); + + Vm.Wallet memory wallet = vm.createWallet("fork wallet"); + + loadConfig(); + (StakeManager stakeManagerImpl, ValidatorShare validatorShareImpl, DepositManager depositManagerImpl) = deployImplementations(wallet.privateKey); + (bytes memory scheduleBatchPayload, bytes memory executeBatchPayload, bytes32 payloadId) = + createPayload(stakeManagerImpl, validatorShareImpl, depositManagerImpl); + + uint256 balanceStakeManager = maticToken.balanceOf(address(stakeManagerProxy)); + console.log("Initial StakeManager Matic balance: ", balanceStakeManager); + assertNotEq(balanceStakeManager, 0); + uint256 balanceDepositManager = maticToken.balanceOf(address(depositManagerProxy)); + console.log("Initial DepositManager Matic balance: ", balanceDepositManager); + assertNotEq(balanceDepositManager, 0); + + vm.prank(gSafeAddress); + (bool successSchedule, bytes memory dataSchedule) = address(timelock).call(scheduleBatchPayload); + if (successSchedule == false) { + assembly { + revert(add(dataSchedule, 32), mload(dataSchedule)) + } + } + + assertEq(successSchedule, true); + assertEq(timelock.isOperation(payloadId), true); + assertEq(timelock.isOperationPending(payloadId), true); + + vm.warp(block.timestamp + 172_800); + + assertEq(timelock.isOperationReady(payloadId), true); + + vm.prank(gSafeAddress); + + (bool successExecute, bytes memory dataExecute) = address(timelock).call(executeBatchPayload); + if (successExecute == false) { + assembly { + revert(add(dataExecute, 32), mload(dataExecute)) + } + } + assertEq(successExecute, true); + assertEq(timelock.isOperationDone(payloadId), true); + + // Check migrations happened + assertEq(maticToken.balanceOf(address(stakeManagerProxy)), 0); + assertEq(polToken.balanceOf(address(stakeManagerProxy)), balanceStakeManager); + assertEq(maticToken.balanceOf(address(depositManagerProxy)), 0); + assertEq(polToken.balanceOf(address(depositManagerProxy)), balanceDepositManager); + + // Check Registry values + assertEq(registry.contractMap(keccak256("validatorShare")), address(validatorShareImpl)); + assertEq(registry.contractMap(keccak256("pol")), address(polToken)); + assertEq(registry.contractMap(keccak256("matic")), address(maticToken)); + assertEq(registry.contractMap(keccak256("polygonMigration")), migrationAddress); + assertEq(registry.rootToChildToken(address(polToken)), nativeGasTokenAddress); + assertEq(registry.childToRootToken(nativeGasTokenAddress), address(polToken)); + assertEq(registry.isERC721(address(polToken)), false); + + // Check Proxy implementation addresses + assertEq(Proxy(payable(address(stakeManagerProxy))).implementation(), address(stakeManagerImpl)); + assertEq(Proxy(payable(address(depositManagerProxy))).implementation(), address(depositManagerImpl)); + } +} diff --git a/test/helpers/artifacts.js b/test/helpers/artifacts.js index 067bc4f9..0305a13c 100644 --- a/test/helpers/artifacts.js +++ b/test/helpers/artifacts.js @@ -1,5 +1,5 @@ import hardhat from 'hardhat' -const ethers = hardhat.ethers +export const ethers = hardhat.ethers export const RootChain = await ethers.getContractFactory('RootChain') export const RootChainProxy = await ethers.getContractFactory('RootChainProxy') @@ -41,6 +41,7 @@ export const ERC721PredicateBurnOnly = await ethers.getContractFactory('ERC721Pr // tokens export const MaticWETH = await ethers.getContractFactory('MaticWETH') export const TestToken = await ethers.getContractFactory('TestToken') +export const ERC20Permit = await ethers.getContractFactory('ERC20Permit') export const RootERC721 = await ethers.getContractFactory('RootERC721') export const ERC721PlasmaMintable = await ethers.getContractFactory('ERC721PlasmaMintable') export const ExitNFT = await ethers.getContractFactory('ExitNFT') @@ -49,6 +50,7 @@ export const ExitNFT = await ethers.getContractFactory('ExitNFT') export const GnosisSafeProxy = await ethers.getContractFactory('GnosisSafeProxy') export const GnosisSafe = await ethers.getContractFactory('GnosisSafe') export const PolygonMigrationTest = await ethers.getContractFactory('PolygonMigrationTest') +export const PolygonMigration = await ethers.getContractFactory('PolygonMigration') // child chain const borProvider = new ethers.providers.JsonRpcProvider( diff --git a/test/helpers/deployer.js b/test/helpers/deployer.js index 62691d3b..c746a2fa 100644 --- a/test/helpers/deployer.js +++ b/test/helpers/deployer.js @@ -24,6 +24,7 @@ class Deployer { this.eventsHub = await this.deployEventsHub(this.registry.address) this.validatorShareFactory = await contractFactories.ValidatorShareFactory.deploy() this.stakeToken = await contractFactories.TestToken.deploy('Stake Token', 'ST') + this.stakingInfo = await contractFactories.StakingInfo.deploy(this.registry.address) this.slashingManager = await contractFactories.SlashingManager.deploy( this.registry.address, @@ -51,7 +52,7 @@ class Deployer { ]) ) - this.stakeManager = await contractFactories.StakeManager.attach(stakeManagerProxy.address) + this.stakeManager = contractFactories.StakeManager.attach(stakeManagerProxy.address) // TODO cannot alter functions like we used to here, replace usage with actual impl like below // this.buildStakeManagerObject(this.stakeManager, this.governance) await this.governance.update( @@ -83,7 +84,7 @@ class Deployer { return _contracts } - async deployStakeManager(wallets) { + async deployStakeManager(wallets, pol = false) { this.governance = await this.deployGovernance() this.registry = await contractFactories.Registry.deploy(this.governance.address) @@ -93,6 +94,7 @@ class Deployer { this.rootChain = await this.deployRootChain() this.stakingInfo = await contractFactories.StakingInfo.deploy(this.registry.address) this.stakeToken = await contractFactories.TestToken.deploy('Stake Token', 'STAKE') + this.stakingNFT = await contractFactories.StakingNFT.deploy('Matic Validator', 'MV') let stakeManager = await contractFactories.StakeManagerTestable.deploy() @@ -114,7 +116,7 @@ class Deployer { ]) ) - this.stakeManager = await contractFactories.StakeManagerTestable.attach(proxy.address) + this.stakeManager = contractFactories.StakeManagerTestable.attach(proxy.address) this.slashingManager = await contractFactories.SlashingManager.deploy( this.registry.address, this.stakingInfo.address, @@ -125,12 +127,32 @@ class Deployer { await this.updateContractMap(ethUtils.keccak256('stakeManager'), this.stakeManager.address) await this.updateContractMap(ethUtils.keccak256('validatorShare'), this.validatorShare.address) await this.updateContractMap(ethUtils.keccak256('slashingManager'), this.slashingManager.address) + + await this.stakeToken.mint(this.stakeManager.address, web3.utils.toWei('10000000')) + + if (pol){ + this.polToken = await contractFactories.ERC20Permit.deploy('POL', 'POL', '1.1.0') + + this.migration = await contractFactories.PolygonMigration.deploy(this.stakeToken.address, this.polToken.address) + + await this.stakeToken.mint(this.migration.address, web3.utils.toWei('50000000')) + await this.polToken.mint(this.migration.address, web3.utils.toWei('50000000')) + + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('initializePOL', [this.polToken.address, this.migration.address]) + ) + + await this.updateContractMap(ethUtils.keccak256('pol'), this.polToken.address) + } + let _contracts = { rootChainOwner: rootChainOwner, registry: this.registry, rootChain: this.rootChain, stakeManager: this.stakeManager, stakeToken: this.stakeToken, + polToken: this.polToken, slashingManager: this.slashingManager, stakingInfo: this.stakingInfo, governance: this.governance, diff --git a/test/helpers/utils.js b/test/helpers/utils.js index 67f578cd..3089af86 100644 --- a/test/helpers/utils.js +++ b/test/helpers/utils.js @@ -481,6 +481,12 @@ export function assertInLogs(logs, eventName, eventArgs = {}) { }) if (event === undefined) { + // If this happens there are multiple events of the same name, sometimes the "correct" one is also broken (in the test) + // so we return the wrong error, in that case log all errors + if (exception.length !== 1){ + console.log("Multiple events found, unsure which is the correct one."); + console.log(exception) + } throw exception[0] } diff --git a/test/integration/root/DepositManagerUpdate.test.js b/test/integration/root/DepositManagerUpdate.test.js index 7e24dd78..5ff10cf6 100644 --- a/test/integration/root/DepositManagerUpdate.test.js +++ b/test/integration/root/DepositManagerUpdate.test.js @@ -86,7 +86,7 @@ describe('DepositManagerUpdate @skip-on-coverage', async function () { // call migrateMatic using governance await governance.update( depositManager.address, - depositManager.interface.encodeFunctionData('migrateMatic', [amount.toString()]) + depositManager.interface.encodeFunctionData('migrateMatic') ) // check that MATIC balance has been converted to POL diff --git a/test/units/root/RootChain.test.js b/test/units/root/RootChain.test.js index 1a1c97be..422c46fe 100644 --- a/test/units/root/RootChain.test.js +++ b/test/units/root/RootChain.test.js @@ -44,15 +44,11 @@ describe('RootChain', async function (accounts) { }) async function freshDeploy() { - const contracts = await deployer.deployStakeManager(wallets) + const contracts = await deployer.deployStakeManager(wallets, true) governance = contracts.governance rootChain = contracts.rootChain stakeManager = contracts.stakeManager - stakeToken = await TestToken.deploy('Stake Token', 'STAKE') - await contracts.governance.update( - stakeManager.address, - stakeManager.interface.encodeFunctionData('setStakingToken', [stakeToken.address]) - ) + stakeToken = contracts.stakeToken await stakeManager.changeRootChain(rootChain.address) await contracts.governance.update( stakeManager.address, diff --git a/test/units/staking/DrainStakeManager.test.js b/test/units/staking/DrainStakeManager.test.js index cffcfccf..fc1a75d5 100644 --- a/test/units/staking/DrainStakeManager.test.js +++ b/test/units/staking/DrainStakeManager.test.js @@ -2,6 +2,7 @@ import chai, { assert } from 'chai' import chaiAsPromised from 'chai-as-promised' +import { BN } from '@openzeppelin/test-helpers' import { DrainStakeManager } from '../../helpers/artifacts.js' @@ -12,21 +13,25 @@ import { generateFirstWallets, mnemonics } from '../../helpers/wallets.js' chai.use(chaiAsPromised).should() -describe('DrainStakeManager', async function (accounts) { - accounts = await ethers.getSigners() - accounts = accounts.map((account) => { - return account.address +// todo: is this even needed anymore? +describe('DrainStakeManager', function (accounts) { + let owner + before(async function() { + accounts = await ethers.getSigners() + accounts = accounts.map((account) => { + return account.address + }) + owner = accounts[0] }) - - const owner = accounts[0] - describe('Upgrade and drain staking contract', async function () { + + describe('Upgrade and drain staking contract', function () { before(async function () { this.wallets = generateFirstWallets(mnemonics, 10) - let contracts = await deployer.deployStakeManager(this.wallets) + let contracts = await deployer.deployStakeManager(this.wallets, true) this.governance = contracts.governance - this.stakeToken = contracts.stakeToken + this.polToken = contracts.polToken this.stakeManager = contracts.stakeManager this.proxy = contracts.stakeManagerProxy this.stakeManagerImpl = contracts.stakeManagerImpl @@ -36,9 +41,10 @@ describe('DrainStakeManager', async function (accounts) { }) it('must have some tokens', async function () { + const initalAmount = web3.utils.toWei('10000000') const amount = web3.utils.toWei('90000') - await this.stakeToken.mint(this.stakeManager.address, amount) - ;(await this.stakeToken.balanceOf(this.stakeManager.address)).toString().should.be.equal(amount.toString()) + await this.polToken.mint(this.stakeManager.address, amount) + ;(await this.polToken.balanceOf(this.stakeManager.address)).toString().should.be.equal(BN(amount).add(BN(initalAmount)).toString()) }) it('must lock stake manager', async function () { @@ -60,7 +66,7 @@ describe('DrainStakeManager', async function (accounts) { }) it('must fail draining when not drained owner', async function () { - const balance = await this.stakeToken.balanceOf(this.stakeManager.address) + const balance = await this.polToken.balanceOf(this.stakeManager.address) try { await this.stakeManagerDrainable.drain(owner, balance) assert.fail('Funds should not be drained') @@ -70,10 +76,10 @@ describe('DrainStakeManager', async function (accounts) { }) it('must drain all funds when drained by owner (Gnosis safe)', async function () { - const balance = await this.stakeToken.balanceOf(this.stakeManager.address) + const balance = await this.polToken.balanceOf(this.stakeManager.address) const data = this.stakeManagerDrainable.interface.encodeFunctionData('drain', [owner, balance.toString()]) await execSafe(this.gSafe, this.stakeManager.address, data, [accounts[1], accounts[2]]) - assert.equal((await this.stakeToken.balanceOf(this.stakeManager.address)).toString(), '0') + assert.equal((await this.polToken.balanceOf(this.stakeManager.address)).toString(), '0') }) it('must swap back to normal implementaion', async function () { diff --git a/test/units/staking/SlashingManager.test.js b/test/units/staking/SlashingManager.test.js index c8f4be66..8c0c2803 100644 --- a/test/units/staking/SlashingManager.test.js +++ b/test/units/staking/SlashingManager.test.js @@ -18,7 +18,8 @@ import * as chai from 'chai' chai.use(chaiAsPromised).should() const assert = chai.assert -describe('Slashing:validator', async function () { +// Slashing is currently disabled in StakeManager, so all these tests fail. +describe.skip('Slashing:validator', async function () { let stakeToken let stakeManager let slashingManager @@ -205,7 +206,7 @@ describe('Slashing:validator', async function () { }) }) }) -describe('Slashing:delegation', async function () { +describe.skip('Slashing:delegation', async function () { let stakeToken let stakeManager let slashingManager diff --git a/test/units/staking/ValidatorShare.test.js b/test/units/staking/ValidatorShare.test.js index f48d755d..375bfebd 100644 --- a/test/units/staking/ValidatorShare.test.js +++ b/test/units/staking/ValidatorShare.test.js @@ -1,8 +1,9 @@ -import { TestToken, ValidatorShare, StakingInfo, EventsHub } from '../../helpers/artifacts.js' +import ethUtils from 'ethereumjs-util' +import { TestToken, ERC20Permit, ValidatorShare, StakingInfo, EventsHub, ethers } from '../../helpers/artifacts.js' import testHelpers from '@openzeppelin/test-helpers' -import { checkPoint, assertBigNumberEquality, updateSlashedAmounts, assertInTransaction } from '../../helpers/utils.js' +import { checkPoint, assertBigNumberEquality, assertInTransaction } from '../../helpers/utils.js' import { wallets, freshDeploy, approveAndStake } from './deployment.js' -import { buyVoucher, sellVoucher, sellVoucherNew } from './ValidatorShareHelper.js' +import { buyVoucher, buyVoucherWithPermit, sellVoucher, sellVoucherNew } from './ValidatorShareHelper.js' const BN = testHelpers.BN const expectRevert = testHelpers.expectRevert const toWei = web3.utils.toWei @@ -14,26 +15,17 @@ const ValidatorDefaultStake = new BN(toWei('100')) describe('ValidatorShare', async function () { const wei100 = toWei('100') - async function slash(slashes = [], validators = [], proposer = wallets[1], nonce = 1) { - let slashingInfoList = [] - for (const slash of slashes) { - slashingInfoList.push([parseInt(slash.validator), slash.amount, '0x0']) - } + // async function slash(slashes = [], validators = [], proposer = wallets[1], nonce = 1) { + // let slashingInfoList = [] + // for (const slash of slashes) { + // slashingInfoList.push([parseInt(slash.validator), slash.amount, '0x0']) + // } - return updateSlashedAmounts(validators, proposer, nonce, slashingInfoList, this.slashingManager) - } + // return updateSlashedAmounts(validators, proposer, nonce, slashingInfoList, this.slashingManager) + // } async function doDeploy() { - await freshDeploy.call(this) - - this.stakeToken = await TestToken.deploy('MATIC', 'MATIC') - - await this.governance.update( - this.stakeManager.address, - this.stakeManager.interface.encodeFunctionData('setStakingToken', [this.stakeToken.address]) - ) - - await this.stakeToken.mint(this.stakeManager.address, toWei('10000000')) + await freshDeploy.call(this, true) this.validatorId = '8' this.validatorUser = wallets[0] @@ -76,7 +68,7 @@ describe('ValidatorShare', async function () { await this.stakeManager.forceFinalizeCommit() let validator = await this.stakeManager.validators(this.validatorId) - this.validatorContract = await ValidatorShare.attach(validator.contractAddress) + this.validatorContract = ValidatorShare.attach(validator.contractAddress) } describe('locking', function () { @@ -143,6 +135,162 @@ describe('ValidatorShare', async function () { }) } + describe('buyVoucherWithPermit', function () { + function testBuyVoucherWithPermit({ + voucherValue, + voucherValueExpected, + userTotalStaked, + totalStaked, + shares, + reward, + initialBalance + }) { + it('must buy voucher with permit', async function () { + assertBigNumberEquality(await this.polToken.allowance(this.user, this.stakeManager.address), 0) + this.receipt = await ( + await buyVoucherWithPermit( + this.validatorContract, + voucherValue, + this.user, + shares, + this.stakeManager.address, + this.polToken + ) + ).wait() + }) + + shouldBuyShares({ + voucherValueExpected, + shares, + totalStaked + }) + + shouldHaveCorrectStakes({ + userTotalStaked, + totalStaked + }) + + shouldWithdrawReward({ + initialBalance, + reward, + validatorId: '8', + pol: true + }) + } + + describe('when Alice purchases voucher with permit once', function () { + deployAliceAndBob() + + before(async function () { + this.user = this.alice + await this.polToken + .connect(this.polToken.provider.getSigner(this.user)) + .approve(this.stakeManager.address, 0) + }) + + testBuyVoucherWithPermit({ + voucherValue: toWei('100'), + voucherValueExpected: toWei('100'), + userTotalStaked: toWei('100'), + totalStaked: toWei('200'), + shares: toWei('100'), + reward: '0', + initialBalance: toWei('705') + }) + }) + + describe('when alice provides invalid permit signature', function () { + deployAliceAndBob() + + before(async function () { + this.user = this.alice + await this.polToken + .connect(this.polToken.provider.getSigner(this.user)) + .approve(this.stakeManager.address, 0) + }) + + it('reverts with incorrect spender', async function () { + assertBigNumberEquality(await this.polToken.allowance(this.user, this.stakeManager.address), 0) + + await expectRevert( + buyVoucherWithPermit( + this.validatorContract, + toWei('1000'), + this.user, + toWei('1000'), + this.validatorContract.address /* spender, tokens are pulled from stakeManager */, + this.polToken + ), + 'ERC2612InvalidSigner' + ) + }) + + it('reverts with incorrect deadline', async function () { + assertBigNumberEquality(await this.polToken.allowance(this.user, this.stakeManager.address), 0) + + await expectRevert( + buyVoucherWithPermit( + this.validatorContract, + toWei('1000'), + this.user, + toWei('1000'), + this.stakeManager.address, + this.polToken, + (await this.validatorContract.provider.getBlock('latest')).timestamp - 60 + ), + 'ERC2612ExpiredSignature' + ) + }) + }) + + describe('when locked', function () { + deployAliceAndBob() + + before(async function () { + await this.stakeManager.testLockShareContract(this.validatorId, true) + }) + + it('reverts', async function () { + await expectRevert( + buyVoucherWithPermit( + this.validatorContract, + toWei('100'), + this.alice, + toWei('100'), + this.stakeManager.address, + this.polToken + ), + 'locked' + ) + }) + }) + + describe('when validator unstaked', function () { + deployAliceAndBob() + before(async function () { + const stakeManagerValidator = this.stakeManager.connect( + this.stakeManager.provider.getSigner(this.validatorUser.getChecksumAddressString()) + ) + await stakeManagerValidator.unstake(this.validatorId) + await this.stakeManager.advanceEpoch(Dynasty) + }) + + it('reverts', async function () { + await expectRevert( + buyVoucherWithPermit( + this.validatorContract, + toWei('100'), + this.alice, + toWei('100'), + this.stakeManager.address, + this.polToken + ), + 'locked' + ) + }) + }) + }) + describe('buyVoucher', function () { function testBuyVoucher({ voucherValue, @@ -189,7 +337,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('200'), shares: toWei('100'), reward: '0', - initialBalance: toWei('69900') + initialBalance: toWei('70705') }) }) @@ -207,7 +355,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('200'), shares: toWei('100'), reward: '0', - initialBalance: toWei('69900') + initialBalance: toWei('70705') }) }) @@ -259,7 +407,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('200'), shares: toWei('100'), reward: '0', - initialBalance: toWei('69900') + initialBalance: toWei('70705') }) }) @@ -271,7 +419,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('350'), shares: toWei('150'), reward: '0', - initialBalance: toWei('69750') + initialBalance: toWei('70555') }) }) @@ -283,7 +431,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('600'), shares: toWei('250'), reward: '0', - initialBalance: toWei('69500') + initialBalance: toWei('70305') }) }) }) @@ -311,7 +459,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('200'), shares: toWei('100'), reward: '0', - initialBalance: toWei('69900') + initialBalance: toWei('70705') }) }) @@ -325,7 +473,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('350'), shares: toWei('150'), reward: toWei('4500'), - initialBalance: toWei('69750') + initialBalance: toWei('70555') }) }) @@ -337,7 +485,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('600'), shares: toWei('250'), reward: '6428571428571428571428', - initialBalance: toWei('74000') + initialBalance: toWei('74805') }) }) }) @@ -357,7 +505,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('200'), shares: toWei('100'), reward: '0', - initialBalance: toWei('69900') + initialBalance: toWei('70705') }) }) @@ -373,7 +521,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('300'), shares: toWei('100'), reward: '0', - initialBalance: toWei('69900') + initialBalance: toWei('70750') }) }) @@ -389,7 +537,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('500'), shares: toWei('200'), reward: '0', - initialBalance: toWei('69700') + initialBalance: toWei('70505') }) }) @@ -405,7 +553,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('700'), shares: toWei('200'), reward: '0', - initialBalance: toWei('69700') + initialBalance: toWei('70550') }) }) }) @@ -432,7 +580,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('200'), shares: toWei('100'), reward: '0', - initialBalance: toWei('69900') + initialBalance: toWei('70705') }) }) @@ -449,7 +597,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('300'), shares: toWei('100'), reward: '0', - initialBalance: toWei('69900') + initialBalance: toWei('70750') }) }) @@ -466,7 +614,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('500'), shares: toWei('200'), reward: toWei('7500'), - initialBalance: toWei('69700') + initialBalance: toWei('70505') }) }) @@ -482,7 +630,7 @@ describe('ValidatorShare', async function () { totalStaked: toWei('700'), shares: toWei('200'), reward: toWei('4800'), - initialBalance: toWei('69700') + initialBalance: toWei('70550') }) }) }) @@ -578,7 +726,8 @@ describe('ValidatorShare', async function () { }) }) - describe('when Alice is slashed by 50% and then Bob purchases voucher (both buy 100 eth worth of shares)', function () { + // slashing currently disabled + describe.skip('when Alice is slashed by 50% and then Bob purchases voucher (both buy 100 eth worth of shares)', function () { before(doDeploy) describe('when Alice is slashed by 50%', function () { @@ -634,7 +783,7 @@ describe('ValidatorShare', async function () { }) }) - describe('when all tokens are slashed', function () { + describe.skip('when all tokens are slashed', function () { before(doDeploy) before(async function () { this.user = wallets[2].getAddressString() @@ -799,7 +948,7 @@ describe('ValidatorShare', async function () { testSellVoucher({ returnedStake: aliceStake, reward: toWei('18000'), - initialBalance: new BN(0), + initialBalance: toWei('805'), validatorId: '8', user: Alice, userTotalStaked: toWei('0'), @@ -808,7 +957,8 @@ describe('ValidatorShare', async function () { }) }) - describe('when Alice sells voucher after 50% slash', function () { + // slashing currently disabled + describe.skip('when Alice sells voucher after 50% slash', function () { before(doDeployAndBuyVoucherForAliceAndBob) before('slash', async function () { await slash.call( @@ -833,7 +983,8 @@ describe('ValidatorShare', async function () { }) }) - describe('when all tokens are slashed', function () { + // slashing currently disabled + describe.skip('when all tokens are slashed', function () { before(doDeployAndBuyVoucherForAliceAndBob) before('slash', async function () { await slash.call( @@ -866,7 +1017,7 @@ describe('ValidatorShare', async function () { testSellVoucher({ returnedStake: aliceStake, reward: toWei('18000'), - initialBalance: new BN(0), + initialBalance: toWei('805'), validatorId: '8', user: Alice, userTotalStaked: toWei('0'), @@ -895,7 +1046,7 @@ describe('ValidatorShare', async function () { testSellVoucher({ returnedStake: aliceStake, reward: toWei('18000'), - initialBalance: new BN(0), + initialBalance: toWei('805'), validatorId: '8', user: Alice, userTotalStaked: toWei('0'), @@ -917,7 +1068,7 @@ describe('ValidatorShare', async function () { testSellVoucher({ returnedStake: aliceStake, reward: toWei('18000'), - initialBalance: new BN(0), + initialBalance: toWei('805'), validatorId: '8', user: Alice, userTotalStaked: toWei('0'), @@ -935,7 +1086,7 @@ describe('ValidatorShare', async function () { testSellVoucher({ returnedStake: aliceStake, reward: toWei('9000'), - initialBalance: new BN(0), + initialBalance: toWei('805'), validatorId: '8', user: Alice, userTotalStaked: toWei('0'), @@ -948,7 +1099,7 @@ describe('ValidatorShare', async function () { testSellVoucher({ returnedStake: bobStake, reward: toWei('18000'), - initialBalance: new BN(0), + initialBalance: toWei('1200'), validatorId: '8', user: Bob, userTotalStaked: toWei('0'), @@ -971,7 +1122,7 @@ describe('ValidatorShare', async function () { minClaimAmount: halfStake, returnedStake: halfStake, reward: toWei('18000'), - initialBalance: new BN(0), + initialBalance: toWei('805'), validatorId: '8', user: Alice, userTotalStaked: halfStake, @@ -990,7 +1141,7 @@ describe('ValidatorShare', async function () { minClaimAmount: halfStake, returnedStake: halfStake, reward: '0', - initialBalance: new BN(toWei('18000')), + initialBalance: new BN(toWei('18805')), validatorId: '8', user: Alice, userTotalStaked: '0', @@ -1000,7 +1151,8 @@ describe('ValidatorShare', async function () { }) }) - describe('when Alice is slashed by 50%', function () { + // slashing currently disabled + describe.skip('when Alice is slashed by 50%', function () { before(doDeployAndBuyVoucherForAliceAndBob) before(async function () { await slash.call( @@ -1021,7 +1173,7 @@ describe('ValidatorShare', async function () { minClaimAmount: halfStake, returnedStake: halfStake, reward: toWei('18000'), - initialBalance: new BN(0), + initialBalance: toWei('805'), validatorId: '8', user: Alice, nonce: '1', @@ -1040,7 +1192,7 @@ describe('ValidatorShare', async function () { minClaimAmount: halfStake, returnedStake: halfStake, reward: '0', - initialBalance: new BN(toWei('18000')), + initialBalance: new BN(toWei('18805')), validatorId: '8', user: Alice, userTotalStaked: '0', @@ -1063,7 +1215,7 @@ describe('ValidatorShare', async function () { minClaimAmount: halfStake, returnedStake: halfStake, reward: toWei('18000'), - initialBalance: new BN(0), + initialBalance: toWei('805'), validatorId: '8', user: Alice, userTotalStaked: halfStake, @@ -1081,7 +1233,7 @@ describe('ValidatorShare', async function () { minClaimAmount: halfStake, returnedStake: halfStake, reward: '0', - initialBalance: new BN(toWei('18000')), + initialBalance: new BN(toWei('18805')), validatorId: '8', user: Alice, userTotalStaked: '0', @@ -1095,7 +1247,8 @@ describe('ValidatorShare', async function () { }) }) - describe('when Alice is slashed by 50%', function () { + // slashing currently disabled + describe.skip('when Alice is slashed by 50%', function () { before(doDeployAndBuyVoucherForAliceAndBob) before(async function () { await slash.call( @@ -1152,13 +1305,13 @@ describe('ValidatorShare', async function () { }) describe('withdrawRewards', function () { - const Alice = wallets[2].getChecksumAddressString() - const Bob = wallets[3].getChecksumAddressString() - const Eve = wallets[4].getChecksumAddressString() - const Carol = wallets[5].getChecksumAddressString() + const Alice = wallets[5].getChecksumAddressString() + const Bob = wallets[6].getChecksumAddressString() + const Eve = wallets[7].getChecksumAddressString() + const Carol = wallets[8].getChecksumAddressString() let totalDelegatorRewardsReceived - let totalSlashed + //let totalSlashed let totalStaked let totalInitialBalance let delegators = [] @@ -1186,20 +1339,20 @@ describe('ValidatorShare', async function () { }) } - function testSlash({ amount, nonce }) { - describe('Slash', function () { - it(`${amount.toString()} wei`, async function () { - await slash.call( - this, - [{ validator: this.validatorId, amount: amount }], - [this.validatorUser], - this.validatorUser, - nonce - ) - totalSlashed = totalSlashed.add(new BN(amount)) - }) - }) - } + // function testSlash({ amount, nonce }) { + // describe('Slash', function () { + // it(`${amount.toString()} wei`, async function () { + // await slash.call( + // this, + // [{ validator: this.validatorId, amount: amount }], + // [this.validatorUser], + // this.validatorUser, + // nonce + // ) + // totalSlashed = totalSlashed.add(new BN(amount)) + // }) + // }) + // } function testStake({ user, amount, label, initialBalance = new BN(0) }) { describe(`${label} buyVoucher for ${amount.toString()} wei`, function () { @@ -1207,7 +1360,7 @@ describe('ValidatorShare', async function () { totalInitialBalance = totalInitialBalance.add(initialBalance) totalStaked = totalStaked.add(new BN(amount)) - await this.stakeToken.mint(user, amount.toString()) + await this.stakeToken.mint(user, initialBalance.add(amount).toString()) const stakeTokenUser = this.stakeToken.connect(this.stakeToken.provider.getSigner(user)) await stakeTokenUser.approve(this.stakeManager.address, amount.toString()) await buyVoucher(this.validatorContract, amount.toString(), user) @@ -1274,14 +1427,13 @@ describe('ValidatorShare', async function () { ) await stakeManagerValidator.withdrawRewards(this.validatorId) - const tokensLeft = await this.stakeToken.balanceOf(this.stakeManager.address) + const tokensLeft = await this.polToken.balanceOf(this.stakeManager.address) assertBigNumberEquality( this.initialStakeTokenBalance .add(totalStaked.toString()) - .sub(totalReceived) - .sub(totalInitialBalance.toString()) - .sub(totalSlashed.toString()), + .sub(totalReceived), + //.sub(totalSlashed.toString()), tokensLeft ) }) @@ -1294,15 +1446,16 @@ describe('ValidatorShare', async function () { delegators = {} totalInitialBalance = new BN(0) totalStaked = new BN(0) - totalSlashed = new BN(0) + //totalSlashed = new BN(0) totalDelegatorRewardsReceived = new BN(0) - this.initialStakeTokenBalance = await this.stakeToken.balanceOf(this.stakeManager.address) + this.initialStakeTokenBalance = await this.polToken.balanceOf(this.stakeManager.address) }) for (const step of timeline) { - if (step.slash) { - testSlash(step.slash) - } else if (step.stake) { + // if (step.slash) { + // testSlash(step.slash) + // } else + if (step.stake) { testStake(step.stake) } else if (step.checkpoints) { testCheckpoint(step.checkpoints) @@ -1334,7 +1487,8 @@ describe('ValidatorShare', async function () { ]) }) - describe('when Alice is slashed by 50% and Bob purchases voucher after', function () { + //slashing currently disabled + describe.skip('when Alice is slashed by 50% and Bob purchases voucher after', function () { describe('when 1 checkpoint passes', function () { describe('when Alice and Bob withdraw rewards after', function () { runWithdrawRewardsTest([ @@ -1366,7 +1520,7 @@ describe('ValidatorShare', async function () { }) }) - describe('when Alice is slashed after checkpoint', function () { + describe.skip('when Alice is slashed after checkpoint', function () { runWithdrawRewardsTest([ { stake: { user: Alice, label: 'Alice', amount: new BN(wei100) } }, { checkpoints: 1 }, @@ -1383,27 +1537,25 @@ describe('ValidatorShare', async function () { { checkpoints: 1 }, { liquidRewards: { user: Alice, label: 'Alice', expectedReward: toWei('4500') } }, { stake: { user: Bob, label: 'Bob', amount: new BN(toWei('500')) } }, - { slash: { amount: new BN(toWei('350')) } }, { checkpoints: 1 }, { liquidRewards: { user: Alice, label: 'Alice', expectedReward: '5785714285714285714285' } }, { liquidRewards: { user: Bob, label: 'Bob', expectedReward: '6428571428571428571428' } }, { stake: { user: Carol, label: 'Carol', amount: new BN(toWei('500')) } }, { checkpoints: 1 }, - { liquidRewards: { user: Alice, label: 'Alice', expectedReward: '6315126050420168067226' } }, - { liquidRewards: { user: Bob, label: 'Bob', expectedReward: '9075630252100840336133' } }, - { liquidRewards: { user: Carol, label: 'Carol', expectedReward: '5294117647058823529411' } }, - { slash: { amount: new BN(toWei('167')), nonce: 2 } }, + { liquidRewards: { user: Alice, label: 'Alice', expectedReward: '6535714285714285714285' } }, + { liquidRewards: { user: Bob, label: 'Bob', expectedReward: '10178571428571428571428' } }, + { liquidRewards: { user: Carol, label: 'Carol', expectedReward: '3750000000000000000000' } }, { stake: { user: Eve, label: 'Eve', amount: new BN(toWei('500')), initialBalance: new BN(1) } }, { checkpoints: 1 }, - { withdraw: { user: Alice, label: 'Alice', expectedReward: '6620779672815871910894' } }, - { withdraw: { user: Bob, label: 'Bob', expectedReward: '10603898364079359554473' } }, - { withdraw: { user: Carol, label: 'Carol', expectedReward: '8350653871015861966090' } }, - { withdraw: { user: Eve, label: 'Eve', expectedReward: '3803888419273034657649', initialBalance: new BN(1) } }, - { allRewards: { validatorReward: '6620779672815871910888', totalExpectedRewards: '35999999999999999999994' } } + { withdraw: { user: Alice, label: 'Alice', expectedReward: '7065126050420168067226' } }, + { withdraw: { user: Bob, label: 'Bob', expectedReward: '12825630252100840336133' } }, + { withdraw: { user: Carol, label: 'Carol', expectedReward: '6397058823529411764705' } }, + { withdraw: { user: Eve, label: 'Eve', expectedReward: '2647058823529411764705', initialBalance: new BN(1) } }, + { allRewards: { validatorReward: '7065126050420168067226', totalExpectedRewards: '35999999999999999999995' } } ]) }) - describe('Alice, Bob, Eve and Carol stake #2', function () { + describe.skip('Alice, Bob, Eve and Carol stake #2', function () { runWithdrawRewardsTest([ { stake: { user: Alice, label: 'Alice', amount: new BN(toWei('100')) } }, { checkpoints: 1 }, @@ -1503,7 +1655,8 @@ describe('ValidatorShare', async function () { }) }) - describe('when all tokens are slashed', function () { + // slashing is currently disabled + describe.skip('when all tokens are slashed', function () { before(doDeploy) before('slash', async function () { const amount = toWei('100') @@ -1977,7 +2130,7 @@ function shouldBuyShares({ shares, voucherValueExpected, totalStaked }) { }) } -function shouldWithdrawReward({ initialBalance, validatorId, user, reward, checkBalance = true }) { +function shouldWithdrawReward({ initialBalance, validatorId, user, reward, checkBalance = true, pol = false }) { if (reward > 0) { it('must emit Transfer', async function () { assertInTransaction(this.receipt, TestToken, 'Transfer', { @@ -1997,10 +2150,17 @@ function shouldWithdrawReward({ initialBalance, validatorId, user, reward, check } if (checkBalance) { - it('must have updated balance', async function () { - const balance = await this.stakeToken.balanceOf(user || this.user) - assertBigNumberEquality(balance, new BN(initialBalance).add(new BN(reward))) - }) + if (pol) { + it('must have updated balance', async function () { + const balance = await this.polToken.balanceOf(user || this.user) + assertBigNumberEquality(balance, new BN(initialBalance).add(new BN(reward))) + }) + } else { + it('must have updated balance', async function () { + const balance = await this.stakeToken.balanceOf(user || this.user) + assertBigNumberEquality(balance, new BN(initialBalance).add(new BN(reward))) + }) + } } it('must have liquid rewards == 0', async function () { diff --git a/test/units/staking/ValidatorShareHelper.js b/test/units/staking/ValidatorShareHelper.js index ec557c52..7f86a55e 100644 --- a/test/units/staking/ValidatorShareHelper.js +++ b/test/units/staking/ValidatorShareHelper.js @@ -1,8 +1,33 @@ +import { getPermitDigest } from './permitHelper.js' + export async function buyVoucher(validatorContract, amount, delegator, minSharesToMint) { const validatorContract_Delegator = validatorContract.connect(validatorContract.provider.getSigner(delegator)) return validatorContract_Delegator.buyVoucher(amount.toString(), minSharesToMint || 0) } +export async function buyVoucherWithPermit( + validatorContract, + amount, + delegator, + minSharesToMint, + spender, + token, + deadline +) { + const signer = validatorContract.provider.getSigner(delegator) + const validatorContract_Delegator = validatorContract.connect(signer) + + if (!deadline) deadline = (await validatorContract.provider.getBlock('latest')).timestamp + 10 + + const signature = await signer._signTypedData(...(await getPermitDigest(delegator, spender, amount, token, deadline))) + + const r = signature.slice(0, 66) + const s = '0x' + signature.slice(66, 130) + const v = '0x' + signature.slice(130, 132) + + return validatorContract_Delegator.buyVoucherWithPermit(amount.toString(), minSharesToMint || 0, deadline, v, r, s) +} + export async function sellVoucher(validatorContract, delegator, minClaimAmount, maxShares) { if (maxShares === undefined) { maxShares = await validatorContract.balanceOf(delegator) @@ -29,3 +54,35 @@ export async function sellVoucherNew(validatorContract, delegator, minClaimAmoun return validatorContract_Delegator.sellVoucher_new(minClaimAmount.toString(), maxShares) } + +export async function buyVoucherPOL(validatorContract, amount, delegator, minSharesToMint) { + const validatorContract_Delegator = validatorContract.connect(validatorContract.provider.getSigner(delegator)) + return validatorContract_Delegator.buyVoucherPOL(amount.toString(), minSharesToMint || 0) +} + +export async function sellVoucherPOL(validatorContract, delegator, minClaimAmount, maxShares) { + if (maxShares === undefined) { + maxShares = await validatorContract.balanceOf(delegator) + } + + if (minClaimAmount === undefined) { + minClaimAmount = await validatorContract.amountStaked(delegator) + } + + const validatorContract_Delegator = validatorContract.connect(validatorContract.provider.getSigner(delegator)) + + return validatorContract_Delegator.sellVoucherPOL(minClaimAmount, maxShares) +} + +export async function sellVoucherNewPOL(validatorContract, delegator, minClaimAmount, maxShares) { + if (maxShares === undefined) { + maxShares = await validatorContract.balanceOf(delegator) + } + + if (minClaimAmount === undefined) { + minClaimAmount = await validatorContract.amountStaked(delegator) + } + const validatorContract_Delegator = validatorContract.connect(validatorContract.provider.getSigner(delegator)) + + return validatorContract_Delegator.sellVoucher_newPOL(minClaimAmount.toString(), maxShares) +} diff --git a/test/units/staking/ValidatorSharePol.test.js b/test/units/staking/ValidatorSharePol.test.js new file mode 100644 index 00000000..8079aff4 --- /dev/null +++ b/test/units/staking/ValidatorSharePol.test.js @@ -0,0 +1,1643 @@ +import { + PolygonMigration, + TestToken, + ERC20Permit, + ValidatorShare, + StakingInfo, + EventsHub +} from '../../helpers/artifacts.js' +import testHelpers from '@openzeppelin/test-helpers' +import { checkPoint, assertBigNumberEquality, assertInTransaction } from '../../helpers/utils.js' +import { wallets, freshDeploy, approveAndStake } from './deployment.js' +import { buyVoucherPOL, sellVoucherPOL, sellVoucherNewPOL } from './ValidatorShareHelper.js' +const BN = testHelpers.BN +const expectRevert = testHelpers.expectRevert +const toWei = web3.utils.toWei +const ZeroAddr = '0x0000000000000000000000000000000000000000' +const ExchangeRatePrecision = new BN('100000000000000000000000000000') +const Dynasty = 8 +const ValidatorDefaultStake = new BN(toWei('100')) + +describe('ValidatorSharePOL', function () { + const wei100 = toWei('100') + + async function doDeploy() { + await freshDeploy.call(this) + + this.polToken = await ERC20Permit.deploy('POL', 'POL', '1.1.0') + this.stakeToken = await TestToken.deploy('MATIC', 'MATIC') + + this.migration = await PolygonMigration.deploy(this.stakeToken.address, this.polToken.address) + + await this.stakeToken.mint(this.migration.address, toWei('30000000')) + await this.polToken.mint(this.migration.address, toWei('40000000')) + + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('setStakingToken', [this.stakeToken.address]) + ) + + await this.stakeToken.mint(this.stakeManager.address, toWei('10000000')) + + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('initializePOL', [this.polToken.address, this.migration.address]) + ) + + this.validatorId = '8' + this.validatorUser = wallets[0] + this.stakeAmount = ValidatorDefaultStake + + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('updateDynastyValue', [Dynasty]) + ) + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('updateValidatorThreshold', [8]) + ) + + // we need to increase validator id beyond foundation id, repeat 7 times + for (let i = 0; i < 7; ++i) { + await approveAndStake.call(this, { + wallet: this.validatorUser, + stakeAmount: this.stakeAmount, + acceptDelegation: true, + pol: true + }) + + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('forceUnstakePOL', [i + 1]) + ) + await this.stakeManager.forceFinalizeCommit() + await this.stakeManager.advanceEpoch(Dynasty) + const stakeManagerValidator = this.stakeManager.connect( + this.stakeManager.provider.getSigner(this.validatorUser.getChecksumAddressString()) + ) + await stakeManagerValidator.unstakeClaimPOL(i + 1) + await this.stakeManager.resetSignerUsed(this.validatorUser.getChecksumAddressString()) + } + + await approveAndStake.call(this, { + wallet: this.validatorUser, + stakeAmount: this.stakeAmount, + acceptDelegation: true, + pol: true + }) + await this.stakeManager.forceFinalizeCommit() + + let validator = await this.stakeManager.validators(this.validatorId) + this.validatorContract = await ValidatorShare.attach(validator.contractAddress) + } + + + function deployAliceAndBob() { + before(doDeploy) + before('Alice & Bob', async function () { + this.alice = wallets[2].getChecksumAddressString() + this.bob = wallets[3].getChecksumAddressString() + this.totalStaked = new BN(0) + + const mintAmount = new BN(toWei('70000')).toString() + + await this.polToken.mint(this.alice, mintAmount) + const polTokenAlice = this.polToken.connect(this.polToken.provider.getSigner(this.alice)) + await polTokenAlice.approve(this.stakeManager.address, mintAmount) + + await this.polToken.mint(this.bob, mintAmount) + const polTokenBob = this.polToken.connect(this.polToken.provider.getSigner(this.bob)) + await polTokenBob.approve(this.stakeManager.address, mintAmount) + }) + } + + describe('buyVoucher', function () { + function testbuyVoucherPOL({ + voucherValue, + voucherValueExpected, + userTotalStaked, + totalStaked, + shares, + reward, + initialBalance + }) { + it('must buy voucher', async function () { + this.receipt = await (await buyVoucherPOL(this.validatorContract, voucherValue, this.user, shares)).wait() + }) + + shouldBuyShares({ + voucherValueExpected, + shares, + totalStaked + }) + + shouldHaveCorrectStakes({ + userTotalStaked, + totalStaked + }) + + shouldWithdrawReward({ + initialBalance, + reward, + validatorId: '8' + }) + } + + describe('when Alice purchases voucher once', function () { + deployAliceAndBob() + + before(function () { + this.user = this.alice + }) + + testbuyVoucherPOL({ + voucherValue: toWei('100'), + voucherValueExpected: toWei('100'), + userTotalStaked: toWei('100'), + totalStaked: toWei('200'), + shares: toWei('100'), + reward: '0', + initialBalance: toWei('69900') + }) + }) + + describe('when Alice purchases voucher with exact minSharesToMint', function () { + deployAliceAndBob() + + before(function () { + this.user = this.alice + }) + + testbuyVoucherPOL({ + voucherValue: toWei('100'), + voucherValueExpected: toWei('100'), + userTotalStaked: toWei('100'), + totalStaked: toWei('200'), + shares: toWei('100'), + reward: '0', + initialBalance: toWei('69900') + }) + }) + + describe('when validator turns off delegation', function () { + deployAliceAndBob() + + before('disable delegation', async function () { + const stakeManagerValidator = this.stakeManager.connect( + this.stakeManager.provider.getSigner(this.validatorUser.getChecksumAddressString()) + ) + await stakeManagerValidator.updateValidatorDelegation(false) + }) + + it('reverts', async function () { + await expectRevert(buyVoucherPOL(this.validatorContract, toWei('150'), this.alice), 'Delegation is disabled') + }) + }) + + describe('when staking manager delegation is disabled', function () { + deployAliceAndBob() + + before('disable delegation', async function () { + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('setDelegationEnabled', [false]) + ) + }) + + it('reverts', async function () { + await expectRevert( + buyVoucherPOL(this.validatorContract, web3.utils.toWei('150'), this.alice), + 'Delegation is disabled' + ) + }) + }) + + describe('when Alice purchases voucher 3 times in a row, no checkpoints inbetween', function () { + deployAliceAndBob() + + before(function () { + this.user = this.alice + }) + + describe('1st purchase', async function () { + testbuyVoucherPOL({ + voucherValue: toWei('100'), + voucherValueExpected: toWei('100'), + userTotalStaked: toWei('100'), + totalStaked: toWei('200'), + shares: toWei('100'), + reward: '0', + initialBalance: toWei('69900') + }) + }) + + describe('2nd purchase', async function () { + testbuyVoucherPOL({ + voucherValue: toWei('150'), + voucherValueExpected: toWei('150'), + userTotalStaked: toWei('250'), + totalStaked: toWei('350'), + shares: toWei('150'), + reward: '0', + initialBalance: toWei('69750') + }) + }) + + describe('3rd purchase', async function () { + testbuyVoucherPOL({ + voucherValue: toWei('250'), + voucherValueExpected: toWei('250'), + userTotalStaked: toWei('500'), + totalStaked: toWei('600'), + shares: toWei('250'), + reward: '0', + initialBalance: toWei('69500') + }) + }) + }) + + describe('when Alice purchases voucher 3 times in a row, 1 checkpoint inbetween', function () { + function advanceCheckpointAfter() { + after(async function () { + await checkPoint([this.validatorUser], this.rootChainOwner, this.stakeManager) + }) + } + + deployAliceAndBob() + + before(function () { + this.user = this.alice + }) + + describe('1st purchase', async function () { + advanceCheckpointAfter() + + testbuyVoucherPOL({ + voucherValue: toWei('100'), + voucherValueExpected: toWei('100'), + userTotalStaked: toWei('100'), + totalStaked: toWei('200'), + shares: toWei('100'), + reward: '0', + initialBalance: toWei('69900') + }) + }) + + describe('2nd purchase', async function () { + advanceCheckpointAfter() + + testbuyVoucherPOL({ + voucherValue: toWei('150'), + voucherValueExpected: toWei('150'), + userTotalStaked: toWei('250'), + totalStaked: toWei('350'), + shares: toWei('150'), + reward: toWei('4500'), + initialBalance: toWei('69750') + }) + }) + + describe('3rd purchase', async function () { + testbuyVoucherPOL({ + voucherValue: toWei('250'), + voucherValueExpected: toWei('250'), + userTotalStaked: toWei('500'), + totalStaked: toWei('600'), + shares: toWei('250'), + reward: '6428571428571428571428', + initialBalance: toWei('74000') + }) + }) + }) + + describe('when Alice and Bob purchase vouchers, no checkpoints inbetween', function () { + deployAliceAndBob() + + describe('when Alice stakes 1st time', function () { + before(function () { + this.user = this.alice + }) + + testbuyVoucherPOL({ + voucherValue: toWei('100'), + voucherValueExpected: toWei('100'), + userTotalStaked: toWei('100'), + totalStaked: toWei('200'), + shares: toWei('100'), + reward: '0', + initialBalance: toWei('69900') + }) + }) + + describe('when Bob stakes 1st time', function () { + before(function () { + this.user = this.bob + }) + + testbuyVoucherPOL({ + voucherValue: toWei('100'), + voucherValueExpected: toWei('100'), + userTotalStaked: toWei('100'), + totalStaked: toWei('300'), + shares: toWei('100'), + reward: '0', + initialBalance: toWei('69900') + }) + }) + + describe('when Alice stakes 2nd time', function () { + before(function () { + this.user = this.alice + }) + + testbuyVoucherPOL({ + voucherValue: toWei('200'), + voucherValueExpected: toWei('200'), + userTotalStaked: toWei('300'), + totalStaked: toWei('500'), + shares: toWei('200'), + reward: '0', + initialBalance: toWei('69700') + }) + }) + + describe('when Bob stakes 2nd time', function () { + before(function () { + this.user = this.bob + }) + + testbuyVoucherPOL({ + voucherValue: toWei('200'), + voucherValueExpected: toWei('200'), + userTotalStaked: toWei('300'), + totalStaked: toWei('700'), + shares: toWei('200'), + reward: '0', + initialBalance: toWei('69700') + }) + }) + }) + + describe('when Alice and Bob purchase vouchers, 1 checkpoint inbetween', function () { + function advanceCheckpointAfter() { + after(async function () { + await checkPoint([this.validatorUser], this.rootChainOwner, this.stakeManager) + }) + } + + deployAliceAndBob() + + describe('when Alice stakes 1st time', function () { + advanceCheckpointAfter() + before(function () { + this.user = this.alice + }) + + testbuyVoucherPOL({ + voucherValue: toWei('100'), + voucherValueExpected: toWei('100'), + userTotalStaked: toWei('100'), + totalStaked: toWei('200'), + shares: toWei('100'), + reward: '0', + initialBalance: toWei('69900') + }) + }) + + describe('when Bob stakes 1st time', function () { + advanceCheckpointAfter() + before(function () { + this.user = this.bob + }) + + testbuyVoucherPOL({ + voucherValue: toWei('100'), + voucherValueExpected: toWei('100'), + userTotalStaked: toWei('100'), + totalStaked: toWei('300'), + shares: toWei('100'), + reward: '0', + initialBalance: toWei('69900') + }) + }) + + describe('when Alice stakes 2nd time', function () { + advanceCheckpointAfter() + before(function () { + this.user = this.alice + }) + + testbuyVoucherPOL({ + voucherValue: toWei('200'), + voucherValueExpected: toWei('200'), + userTotalStaked: toWei('300'), + totalStaked: toWei('500'), + shares: toWei('200'), + reward: toWei('7500'), + initialBalance: toWei('69700') + }) + }) + + describe('when Bob stakes 2nd time', function () { + before(function () { + this.user = this.bob + }) + + testbuyVoucherPOL({ + voucherValue: toWei('200'), + voucherValueExpected: toWei('200'), + userTotalStaked: toWei('300'), + totalStaked: toWei('700'), + shares: toWei('200'), + reward: toWei('4800'), + initialBalance: toWei('69700') + }) + }) + }) + + describe('when locked', function () { + deployAliceAndBob() + + before(async function () { + await this.stakeManager.testLockShareContract(this.validatorId, true) + }) + + it('reverts', async function () { + await expectRevert(buyVoucherPOL(this.validatorContract, toWei('100'), this.alice, toWei('100')), 'locked') + }) + }) + + describe('when validator unstaked', function () { + deployAliceAndBob() + before(async function () { + const stakeManagerValidator = this.stakeManager.connect( + this.stakeManager.provider.getSigner(this.validatorUser.getChecksumAddressString()) + ) + await stakeManagerValidator.unstake(this.validatorId) + await this.stakeManager.advanceEpoch(Dynasty) + }) + + it('reverts', async function () { + await expectRevert(buyVoucherPOL(this.validatorContract, new BN(toWei('100')), this.alice), 'locked') + }) + }) + }) + + describe('exchangeRate', function () { + describe('when Alice purchases voucher 2 times, 1 epoch between', function () { + before(doDeploy) + + before(async function () { + this.user = wallets[2].getAddressString() + this.totalStaked = new BN(0) + + const voucherAmount = new BN(toWei('70000')).toString() + await this.polToken.mint(this.user, voucherAmount) + const polTokenUser = this.polToken.connect(this.polToken.provider.getSigner(this.user)) + await polTokenUser.approve(this.stakeManager.address, voucherAmount) + }) + + it('must buy voucher', async function () { + const voucherValue = toWei('100') + this.totalStaked = this.totalStaked.add(new BN(voucherValue)) + + await buyVoucherPOL(this.validatorContract, voucherValue, this.user) + }) + + it('exchange rate must be correct', async function () { + assertBigNumberEquality(await this.validatorContract.exchangeRate(), ExchangeRatePrecision) + }) + + it('must buy another voucher 1 epoch later', async function () { + await checkPoint([this.validatorUser], this.rootChainOwner, this.stakeManager) + + const voucherValue = toWei('5000') + this.totalStaked = this.totalStaked.add(new BN(voucherValue)) + await buyVoucherPOL(this.validatorContract, voucherValue, this.user) + }) + + it('exchange rate must be correct', async function () { + assertBigNumberEquality(await this.validatorContract.exchangeRate(), ExchangeRatePrecision) + }) + }) + + describe('when Alice purchases voucher and sells it', function () { + before(doDeploy) + before(async function () { + this.user = wallets[2].getAddressString() + await this.polToken.mint(this.user, toWei('250')) + + this.beforeExchangeRate = await this.validatorContract.exchangeRate() + const polTokenUser = this.polToken.connect(this.polToken.provider.getSigner(this.user)) + await polTokenUser.approve(this.stakeManager.address, toWei('250')) + }) + + it('must purchase voucher', async function () { + await buyVoucherPOL(this.validatorContract, toWei('100'), this.user) + }) + + it('must sell voucher', async function () { + await sellVoucherPOL(this.validatorContract, this.user) + }) + + it('must have initial exchange rate', async function () { + let afterExchangeRate = await this.validatorContract.exchangeRate() + assertBigNumberEquality(afterExchangeRate, this.beforeExchangeRate) + }) + }) + }) + + describe('sellVoucher', function () { + const aliceStake = new BN(toWei('100')) + const bobStake = new BN(toWei('200')) + const Alice = wallets[2].getChecksumAddressString() + const Bob = wallets[1].getChecksumAddressString() + + async function doDeployAndBuyVoucherForAliceAndBob(includeBob = false) { + await doDeploy.call(this) + + const stake = async ({ user, stake }) => { + await this.polToken.mint(user, stake) + const polTokenUser = this.polToken.connect(this.polToken.provider.getSigner(user)) + await polTokenUser.approve(this.stakeManager.address, stake) + await buyVoucherPOL(this.validatorContract, stake, user) + } + + await stake({ user: Alice, stake: aliceStake.toString() }) + + if (includeBob) { + await stake({ user: Bob, stake: bobStake.toString() }) + } + + for (let i = 0; i < 4; i++) { + await checkPoint([this.validatorUser], this.rootChainOwner, this.stakeManager) + } + } + + function testsellVoucherPOLNew({ + returnedStake, + reward, + initialBalance, + validatorId, + user, + minClaimAmount, + userTotalStaked, + totalStaked, + shares, + nonce, + withdrawalExchangeRate = ExchangeRatePrecision + }) { + if (minClaimAmount) { + it('must sell voucher with slippage', async function () { + this.receipt = await (await sellVoucherNewPOL(this.validatorContract, user, minClaimAmount)).wait() + }) + } else { + it('must sell voucher', async function () { + this.receipt = await (await sellVoucherNewPOL(this.validatorContract, user)).wait() + }) + } + + if (nonce) { + it('must emit ShareBurnedWithId', async function () { + assertInTransaction(this.receipt, EventsHub, 'ShareBurnedWithId', { + validatorId: validatorId, + tokens: shares.toString(), + amount: returnedStake.toString(), + user: user, + nonce + }) + }) + } else { + it('must emit ShareBurned', async function () { + assertInTransaction(this.receipt, StakingInfo, 'ShareBurned', { + validatorId: validatorId, + tokens: shares, + amount: returnedStake, + user: user + }) + }) + } + + shouldWithdrawReward({ initialBalance, validatorId, user, reward }) + + shouldHaveCorrectStakes({ + userTotalStaked, + totalStaked, + user + }) + + it('must have correct withdrawal exchange rate', async function () { + const rate = await this.validatorContract.withdrawExchangeRate() + assertBigNumberEquality(rate, withdrawalExchangeRate) + }) + } + + function testsellVoucherPOL({ + returnedStake, + reward, + initialBalance, + validatorId, + user, + minClaimAmount, + userTotalStaked, + totalStaked, + shares, + withdrawalExchangeRate = ExchangeRatePrecision + }) { + if (minClaimAmount) { + it('must sell voucher with slippage', async function () { + this.receipt = await (await sellVoucherPOL(this.validatorContract, user, minClaimAmount.toString())).wait() + }) + } else { + it('must sell voucher', async function () { + this.receipt = await (await sellVoucherPOL(this.validatorContract, user)).wait() + }) + } + + it('must emit ShareBurned', async function () { + assertInTransaction(this.receipt, StakingInfo, 'ShareBurned', { + validatorId: validatorId, + tokens: shares.toString(), + amount: returnedStake.toString(), + user: user + }) + }) + + shouldWithdrawReward({ initialBalance, validatorId, user, reward }) + + shouldHaveCorrectStakes({ + userTotalStaked, + totalStaked, + user + }) + + it('must have correct withdrawal exchange rate', async function () { + const rate = await this.validatorContract.withdrawExchangeRate() + assertBigNumberEquality(rate, withdrawalExchangeRate) + }) + } + + describe('when Alice sells voucher', function () { + before(doDeployAndBuyVoucherForAliceAndBob) + + testsellVoucherPOL({ + returnedStake: aliceStake, + reward: toWei('18000'), + initialBalance: new BN(0), + validatorId: '8', + user: Alice, + userTotalStaked: toWei('0'), + totalStaked: ValidatorDefaultStake, + shares: aliceStake + }) + }) + + describe('when delegation is disabled after voucher was purchased by Alice', function () { + before(doDeployAndBuyVoucherForAliceAndBob) + before('disable delegation', async function () { + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('setDelegationEnabled', [false]) + ) + }) + + testsellVoucherPOL({ + returnedStake: aliceStake, + reward: toWei('18000'), + initialBalance: new BN(0), + validatorId: '8', + user: Alice, + userTotalStaked: toWei('0'), + totalStaked: ValidatorDefaultStake, + shares: aliceStake + }) + }) + + describe('when Alice sells with claimAmount greater than expected', function () { + before(doDeployAndBuyVoucherForAliceAndBob) + + it('reverts', async function () { + const maxShares = await this.validatorContract.balanceOf(Alice) + const validatorAlice = this.validatorContract.connect(this.validatorContract.provider.getSigner(Alice)) + await expectRevert(validatorAlice.sellVoucherPOL(toWei('100.00001'), maxShares), 'Too much requested') + }) + }) + + describe('when locked', function () { + before(doDeployAndBuyVoucherForAliceAndBob) + + before(async function () { + await this.stakeManager.testLockShareContract(this.validatorId, true) + }) + + testsellVoucherPOL({ + returnedStake: aliceStake, + reward: toWei('18000'), + initialBalance: new BN(0), + validatorId: '8', + user: Alice, + userTotalStaked: toWei('0'), + totalStaked: ValidatorDefaultStake, + shares: aliceStake + }) + }) + + describe('when validator unstaked', function () { + before(doDeployAndBuyVoucherForAliceAndBob) + before(async function () { + const stakeManagerValidator = this.stakeManager.connect( + this.stakeManager.provider.getSigner(this.validatorUser.getChecksumAddressString()) + ) + await stakeManagerValidator.unstake(this.validatorId) + await this.stakeManager.advanceEpoch(Dynasty) + }) + + testsellVoucherPOL({ + returnedStake: aliceStake, + reward: toWei('18000'), + initialBalance: new BN(0), + validatorId: '8', + user: Alice, + userTotalStaked: toWei('0'), + totalStaked: 0, + shares: aliceStake + }) + }) + + describe('when Alice and Bob sell within withdrawal delay', function () { + before(async function () { + await doDeployAndBuyVoucherForAliceAndBob.call(this, true) + }) + + describe('when Alice sells', function () { + testsellVoucherPOL({ + returnedStake: aliceStake, + reward: toWei('9000'), + initialBalance: new BN(0), + validatorId: '8', + user: Alice, + userTotalStaked: toWei('0'), + shares: aliceStake, + totalStaked: new BN(bobStake).add(ValidatorDefaultStake) + }) + }) + + describe('when Bob sells', function () { + testsellVoucherPOL({ + returnedStake: bobStake, + reward: toWei('18000'), + initialBalance: new BN(0), + validatorId: '8', + user: Bob, + userTotalStaked: toWei('0'), + shares: bobStake, + totalStaked: ValidatorDefaultStake + }) + }) + }) + + describe('partial sell', function () { + describe('new API', function () { + describe('when Alice is not slashed', function () { + before(doDeployAndBuyVoucherForAliceAndBob) + + const halfStake = aliceStake.div(new BN('2')) + + describe('when Alice sells 50%', function () { + testsellVoucherPOLNew({ + shares: new BN(toWei('50')), + minClaimAmount: halfStake, + returnedStake: halfStake, + reward: toWei('18000'), + initialBalance: new BN(0), + validatorId: '8', + user: Alice, + userTotalStaked: halfStake, + nonce: '1', + totalStaked: halfStake.add(ValidatorDefaultStake) + }) + }) + + describe('when Alice sells 50%, after 1 epoch, within withdrawal delay', function () { + before(async function () { + await this.stakeManager.advanceEpoch(1) + }) + + testsellVoucherPOLNew({ + shares: new BN(toWei('50')), + minClaimAmount: halfStake, + returnedStake: halfStake, + reward: '0', + initialBalance: new BN(toWei('18000')), + validatorId: '8', + user: Alice, + userTotalStaked: '0', + nonce: '2', + totalStaked: ValidatorDefaultStake + }) + }) + }) + }) + + describe('old API', function () { + describe('when Alice is not slashed', function () { + before(doDeployAndBuyVoucherForAliceAndBob) + + const halfStake = aliceStake.div(new BN('2')) + + describe('when Alice sells 50%', function () { + testsellVoucherPOL({ + shares: new BN(toWei('50')).toString(), + minClaimAmount: halfStake, + returnedStake: halfStake, + reward: toWei('18000'), + initialBalance: new BN(0), + validatorId: '8', + user: Alice, + userTotalStaked: halfStake, + totalStaked: halfStake.add(ValidatorDefaultStake) + }) + }) + + describe('when Alice sells 50%, after 1 epoch, within withdrawal delay', function () { + before(async function () { + await this.stakeManager.advanceEpoch(1) + }) + + testsellVoucherPOL({ + shares: new BN(toWei('50')), + minClaimAmount: halfStake, + returnedStake: halfStake, + reward: '0', + initialBalance: new BN(toWei('18000')), + validatorId: '8', + user: Alice, + userTotalStaked: '0', + totalStaked: ValidatorDefaultStake + }) + + it('unbond epoch must be set to current epoch', async function () { + const unbond = await this.validatorContract.unbonds(Alice) + assertBigNumberEquality(unbond.withdrawEpoch, await this.stakeManager.currentEpoch()) + }) + }) + }) + }) + }) + }) + + describe('withdrawRewards', function () { + const Alice = wallets[2].getChecksumAddressString() + const Bob = wallets[3].getChecksumAddressString() + const Eve = wallets[4].getChecksumAddressString() + const Carol = wallets[5].getChecksumAddressString() + + let totalDelegatorRewardsReceived + let totalStaked + let totalInitialBalance + let delegators = [] + + function testWithdraw({ label, user, expectedReward, initialBalance }) { + describe(`when ${label} withdraws`, function () { + if (expectedReward.toString() === '0') { + it('reverts', async function () { + const validatorUser = this.validatorContract.connect(this.validatorContract.provider.getSigner(user)) + await expectRevert(validatorUser.withdrawRewardsPOL(), 'Too small rewards amount') + }) + } else { + it('must withdraw rewards', async function () { + const validatorUser = this.validatorContract.connect(this.validatorContract.provider.getSigner(user)) + this.receipt = await (await validatorUser.withdrawRewardsPOL()).wait() + }) + + shouldWithdrawReward({ + reward: expectedReward, + user: user, + validatorId: '8', + initialBalance: initialBalance + }) + } + }) + } + + function testStake({ user, amount, label, initialBalance = new BN(0) }) { + describe(`${label} buyVoucher for ${amount.toString()} wei`, function () { + it(`must purchase voucher`, async function () { + totalInitialBalance = totalInitialBalance.add(initialBalance) + totalStaked = totalStaked.add(new BN(amount)) + + await this.polToken.mint(user, initialBalance.add(amount).toString()) + const polTokenUser = this.polToken.connect(this.polToken.provider.getSigner(user)) + await polTokenUser.approve(this.stakeManager.address, amount.toString()) + await buyVoucherPOL(this.validatorContract, amount.toString(), user) + delegators[user] = delegators[user] || { + rewards: new BN(0) + } + }) + + it('must have correct initalRewardPerShare', async function () { + const currentRewardPerShare = await this.validatorContract.getRewardPerShare() + const userRewardPerShare = await this.validatorContract.initalRewardPerShare(user) + assertBigNumberEquality(currentRewardPerShare, userRewardPerShare) + }) + }) + } + + function testCheckpoint(checkpoints) { + describe('checkpoints', function () { + it(`${checkpoints} more checkpoint(s)`, async function () { + let c = 0 + while (c++ < checkpoints) { + await checkPoint([this.validatorUser], this.rootChainOwner, this.stakeManager) + } + + totalDelegatorRewardsReceived = new ethers.BigNumber.from(0) + for (const user in delegators) { + const rewards = await this.validatorContract.getLiquidRewards(user) + totalDelegatorRewardsReceived = totalDelegatorRewardsReceived.add(rewards) + } + }) + }) + } + + function testLiquidRewards({ user, label, expectedReward }) { + describe(`${label} liquid rewards`, function () { + it(`${expectedReward.toString()}`, async function () { + const rewards = await this.validatorContract.getLiquidRewards(user) + assertBigNumberEquality(rewards, expectedReward) + }) + }) + } + + function testAllRewardsReceived({ validatorReward, totalExpectedRewards }) { + async function getValidatorReward() { + return this.stakeManager.validatorReward(this.validatorId) + } + + describe('total rewards', function () { + it(`validator rewards == ${validatorReward.toString()}`, async function () { + assertBigNumberEquality(await getValidatorReward.call(this), validatorReward) + }) + + it(`all expected rewards should be ${totalExpectedRewards.toString()}`, async function () { + const validatorRewards = await getValidatorReward.call(this) + assertBigNumberEquality(validatorRewards.add(totalDelegatorRewardsReceived.toString()), totalExpectedRewards) + }) + + it(`total received rewards must be correct`, async function () { + const validatorRewards = await getValidatorReward.call(this) + const totalReceived = validatorRewards.add(totalDelegatorRewardsReceived.toString()) + + const stakeManagerValidator = this.stakeManager.connect( + this.stakeManager.provider.getSigner(this.validatorUser.getChecksumAddressString()) + ) + await stakeManagerValidator.withdrawRewardsPOL(this.validatorId) + + const tokensLeft = await this.polToken.balanceOf(this.stakeManager.address) + + assertBigNumberEquality( + this.initialPolTokenBalance + .add(totalStaked.toString()) + .sub(totalReceived), + tokensLeft + ) + }) + }) + } + + function runWithdrawRewardsTest(timeline) { + before(doDeploy) + before(async function () { + delegators = {} + totalInitialBalance = new BN(0) + totalStaked = new BN(0) + totalDelegatorRewardsReceived = new BN(0) + this.initialPolTokenBalance = await this.polToken.balanceOf(this.stakeManager.address) + }) + + for (const step of timeline) { + if (step.stake) { + testStake(step.stake) + } else if (step.checkpoints) { + testCheckpoint(step.checkpoints) + } else if (step.withdraw) { + testWithdraw(step.withdraw) + } else if (step.liquidRewards) { + testLiquidRewards(step.liquidRewards) + } else if (step.allRewards) { + testAllRewardsReceived(step.allRewards) + } + } + } + + describe('when Alice purchases voucher after checkpoint', function () { + runWithdrawRewardsTest([ + { checkpoints: 1 }, + { stake: { user: Alice, label: 'Alice', amount: new BN(wei100) } }, + { withdraw: { user: Alice, label: 'Alice', expectedReward: '0' } }, + { allRewards: { validatorReward: toWei('9000'), totalExpectedRewards: toWei('9000') } } + ]) + }) + + describe('when Alice is not slashed. 1 checkpoint passed', function () { + runWithdrawRewardsTest([ + { stake: { user: Alice, label: 'Alice', amount: new BN(wei100) } }, + { checkpoints: 1 }, + { withdraw: { user: Alice, label: 'Alice', expectedReward: toWei('4500') } }, + { allRewards: { validatorReward: toWei('4500'), totalExpectedRewards: toWei('9000') } } + ]) + }) + + describe('Alice, Bob, Eve and Carol stake #1', function () { + runWithdrawRewardsTest([ + { stake: { user: Alice, label: 'Alice', amount: new BN(toWei('100')) } }, + { checkpoints: 1 }, + { liquidRewards: { user: Alice, label: 'Alice', expectedReward: toWei('4500') } }, + { stake: { user: Bob, label: 'Bob', amount: new BN(toWei('500')) } }, + { checkpoints: 1 }, + { liquidRewards: { user: Alice, label: 'Alice', expectedReward: '5785714285714285714285' } }, + { liquidRewards: { user: Bob, label: 'Bob', expectedReward: '6428571428571428571428' } }, + { stake: { user: Carol, label: 'Carol', amount: new BN(toWei('500')) } }, + { checkpoints: 1 }, + { liquidRewards: { user: Alice, label: 'Alice', expectedReward: '6535714285714285714285' } }, + { liquidRewards: { user: Bob, label: 'Bob', expectedReward: '10178571428571428571428' } }, + { liquidRewards: { user: Carol, label: 'Carol', expectedReward: '3750000000000000000000' } }, + { stake: { user: Eve, label: 'Eve', amount: new BN(toWei('500')), initialBalance: new BN(1) } }, + { checkpoints: 1 }, + { withdraw: { user: Alice, label: 'Alice', expectedReward: '7065126050420168067226' } }, + { withdraw: { user: Bob, label: 'Bob', expectedReward: '12825630252100840336133' } }, + { withdraw: { user: Carol, label: 'Carol', expectedReward: '6397058823529411764705' } }, + { withdraw: { user: Eve, label: 'Eve', expectedReward: '2647058823529411764705', initialBalance: new BN(1) } }, + { allRewards: { validatorReward: '7065126050420168067226', totalExpectedRewards: '35999999999999999999995' } } + ]) + }) + + describe('when not enough rewards', function () { + before(doDeploy) + + it('reverts', async function () { + const validatorContractAlice = this.validatorContract.connect(this.validatorContract.provider.getSigner(Alice)) + await expectRevert(validatorContractAlice.withdrawRewardsPOL(), 'Too small rewards amount') + }) + }) + + describe('when Alice withdraws 2 times in a row', async function () { + runWithdrawRewardsTest([ + { stake: { user: Alice, label: 'Alice', amount: new BN(toWei('100')) } }, + { checkpoints: 1 }, + { withdraw: { user: Alice, label: 'Alice', expectedReward: toWei('4500') } }, + { withdraw: { user: Alice, label: 'Alice', expectedReward: '0' } } + ]) + }) + + describe('when locked', function () { + before(doDeploy) + + before(async function () { + const amount = toWei('100') + await this.polToken.mint(Alice, amount) + const polTokenAlice = this.polToken.connect(this.polToken.provider.getSigner(Alice)) + await polTokenAlice.approve(this.stakeManager.address, amount) + await buyVoucherPOL(this.validatorContract, amount, Alice) + await checkPoint([this.validatorUser], this.rootChainOwner, this.stakeManager) + await this.stakeManager.testLockShareContract(this.validatorId, true) + }) + + it('must withdraw rewards', async function () { + const validatorContractAlice = this.validatorContract.connect(this.validatorContract.provider.getSigner(Alice)) + this.receipt = await (await validatorContractAlice.withdrawRewardsPOL()).wait() + }) + + shouldWithdrawReward({ + initialBalance: new BN('0'), + validatorId: '8', + user: Alice, + reward: toWei('4500') + }) + }) + + describe('when validator unstaked', function () { + before(doDeploy) + before(async function () { + const amount = toWei('100') + await this.polToken.mint(Alice, amount) + const polTokenAlice = this.polToken.connect(this.polToken.provider.getSigner(Alice)) + await polTokenAlice.approve(this.stakeManager.address, amount) + await buyVoucherPOL(this.validatorContract, amount, Alice) + await checkPoint([this.validatorUser], this.rootChainOwner, this.stakeManager) + const stakeManagerAlice = this.stakeManager.connect( + this.stakeManager.provider.getSigner(this.validatorUser.getChecksumAddressString()) + ) + await stakeManagerAlice.unstake(this.validatorId) + await this.stakeManager.advanceEpoch(Dynasty) + }) + + it('must withdraw rewards', async function () { + const validatorContractAlice = this.validatorContract.connect(this.validatorContract.provider.getSigner(Alice)) + this.receipt = await (await validatorContractAlice.withdrawRewardsPOL()).wait() + }) + + shouldWithdrawReward({ + initialBalance: new BN('0'), + validatorId: '8', + user: Alice, + reward: toWei('4500') + }) + }) + }) + + describe('restake', function () { + function prepareForTest({ skipCheckpoint } = {}) { + before(doDeploy) + before(async function () { + this.user = wallets[2].getChecksumAddressString() + + await this.polToken.mint(this.user, this.stakeAmount.toString()) + const polTokenUser = this.polToken.connect(this.polToken.provider.getSigner(this.user)) + await polTokenUser.approve(this.stakeManager.address, this.stakeAmount.toString()) + + await buyVoucherPOL(this.validatorContract, this.stakeAmount.toString(), this.user) + this.shares = await this.validatorContract.balanceOf(this.user) + + if (!skipCheckpoint) { + await checkPoint([this.validatorUser], this.rootChainOwner, this.stakeManager) + } + }) + } + + describe('when Alice restakes', function () { + const voucherValueExpected = new BN(toWei('4500')) + const reward = new BN(toWei('4500')) + const userTotalStaked = new BN(toWei('4600')) + const shares = new BN(toWei('4500')) + const totalStaked = new BN(toWei('4700')) + const initialBalance = new BN(toWei('100')) + + prepareForTest() + + it('must restake', async function () { + const validatorUser = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.user)) + this.receipt = await (await validatorUser.restakePOL()).wait() + }) + + shouldBuyShares({ + voucherValueExpected, + userTotalStaked, + totalStaked, + shares, + reward, + initialBalance + }) + + shouldWithdrawReward({ + reward: '0', // we need only partial test here, reward is not really claimed + initialBalance, + checkBalance: false, + validatorId: '8' + }) + + it('must emit DelegatorRestaked', async function () { + assertInTransaction(this.receipt, StakingInfo, 'DelegatorRestaked', { + validatorId: this.validatorId, + totalStaked: userTotalStaked.toString() + }) + }) + }) + + describe('when no liquid rewards', function () { + prepareForTest({ skipCheckpoint: true }) + + it('reverts', async function () { + const validatorUser = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.user)) + await expectRevert(validatorUser.restakePOL(), 'Too small rewards to restake') + }) + }) + + describe('when staking manager delegation is disabled', function () { + prepareForTest() + + before('disable delegation', async function () { + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('setDelegationEnabled', [false]) + ) + }) + + it('reverts', async function () { + const validatorUser = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.user)) + await expectRevert(validatorUser.restakePOL(), 'Delegation is disabled') + }) + }) + + describe('when validator unstaked', function () { + prepareForTest() + before(async function () { + const stakeManagerValidator = this.stakeManager.connect( + this.stakeManager.provider.getSigner(this.validatorUser.getChecksumAddressString()) + ) + await stakeManagerValidator.unstake(this.validatorId) + }) + + it('reverts', async function () { + const validatorUser = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.user)) + await expectRevert(validatorUser.restakePOL(), 'locked') + }) + }) + + describe('when locked', function () { + prepareForTest() + + before(async function () { + await this.stakeManager.testLockShareContract(this.validatorId, true) + }) + + it('reverts', async function () { + const validatorUser = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.user)) + await expectRevert(validatorUser.restakePOL(), 'locked') + }) + }) + }) + + describe('unstakeClaimTokens', function () { + function prepareForTest({ skipSell, skipBuy } = {}) { + before(doDeploy) + before(async function () { + this.user = wallets[2].getChecksumAddressString() + + await this.polToken.mint(this.user, this.stakeAmount.toString()) + const polTokenUser = this.polToken.connect(this.polToken.provider.getSigner(this.user)) + await polTokenUser.approve(this.stakeManager.address, this.stakeAmount.toString()) + + this.totalStaked = this.stakeAmount + }) + + if (!skipBuy) { + before('buy', async function () { + await buyVoucherPOL(this.validatorContract, this.stakeAmount.toString(), this.user) + }) + } + + if (!skipSell) { + before('sell', async function () { + await sellVoucherPOL(this.validatorContract, this.user) + }) + } + } + + describe('when Alice unstakes right after voucher sell', function () { + prepareForTest() + + before('checkpoint', async function () { + let currentEpoch = await this.stakeManager.currentEpoch() + let exitEpoch = currentEpoch.add(await this.stakeManager.WITHDRAWAL_DELAY()) + + for (let i = currentEpoch; i <= exitEpoch; i++) { + await checkPoint([this.validatorUser], this.rootChainOwner, this.stakeManager) + } + }) + + it('must unstake', async function () { + const validatorUser = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.user)) + this.receipt = await (await validatorUser.unstakeClaimTokensPOL()).wait() + }) + + it('must emit DelegatorUnstaked', async function () { + assertInTransaction(this.receipt, StakingInfo, 'DelegatorUnstaked', { + validatorId: this.validatorId, + user: this.user, + amount: this.stakeAmount.toString() + }) + }) + + shouldHaveCorrectStakes({ + userTotalStaked: '0', + totalStaked: toWei('100') + }) + }) + + describe('when Alice claims too early', function () { + prepareForTest() + + it('reverts', async function () { + const validatorUser = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.user)) + await expectRevert( + validatorUser.unstakeClaimTokensPOL(), + 'Incomplete withdrawal period' + ) + }) + }) + + describe('when Alice claims with 0 shares', function () { + prepareForTest({ skipSell: true }) + + it('reverts', async function () { + const validatorUser = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.user)) + await expectRevert( + validatorUser.unstakeClaimTokensPOL(), + 'Incomplete withdrawal period' + ) + }) + }) + + describe("when Alice didn't buy voucher", function () { + prepareForTest({ skipSell: true, skipBuy: true }) + + it('reverts', async function () { + const validatorUser = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.user)) + await expectRevert( + validatorUser.unstakeClaimTokensPOL(), + 'Incomplete withdrawal period' + ) + }) + }) + + describe('new API', function () { + describe('when Alice claims 2 seperate unstakes (1 epoch between unstakes)', function () { + prepareForTest({ skipSell: true }) + + before('sell shares twice', async function () { + this.claimAmount = this.stakeAmount.div(new BN('2')) + + await sellVoucherNewPOL(this.validatorContract, this.user, this.claimAmount) + await checkPoint([this.validatorUser], this.rootChainOwner, this.stakeManager) + await sellVoucherNewPOL(this.validatorContract, this.user, this.claimAmount) + }) + + before('checkpoint', async function () { + let currentEpoch = await this.stakeManager.currentEpoch() + let exitEpoch = currentEpoch.add(await this.stakeManager.WITHDRAWAL_DELAY()) + + for (let i = currentEpoch; i <= exitEpoch; i++) { + await checkPoint([this.validatorUser], this.rootChainOwner, this.stakeManager) + } + }) + + it('must claim 1st unstake', async function () { + const validatorUser = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.user)) + this.receipt = await (await validatorUser.unstakeClaimTokens_newPOL('1')).wait() + }) + + it('must emit DelegatorUnstakeWithId', async function () { + assertInTransaction(this.receipt, EventsHub, 'DelegatorUnstakeWithId', { + validatorId: this.validatorId, + user: this.user, + amount: this.claimAmount.toString(), + nonce: '1' + }) + }) + + it('must claim 2nd unstake', async function () { + const validatorUser = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.user)) + this.receipt = await (await validatorUser.unstakeClaimTokens_newPOL('2')).wait() + }) + + it('must emit DelegatorUnstakeWithId', async function () { + assertInTransaction(this.receipt, EventsHub, 'DelegatorUnstakeWithId', { + validatorId: this.validatorId, + user: this.user, + amount: this.claimAmount.toString(), + nonce: '2' + }) + }) + + it('must have 0 shares', async function () { + assertBigNumberEquality(await this.validatorContract.balanceOf(this.user), '0') + }) + }) + }) + }) + + describe('getLiquidRewards', function () { + describe('when Alice and Bob buy vouchers (1 checkpoint in-between) and Alice withdraw the rewards', function () { + deployAliceAndBob() + before(async function () { + await buyVoucherPOL(this.validatorContract, toWei('100'), this.alice) + await checkPoint([this.validatorUser], this.rootChainOwner, this.stakeManager) + await buyVoucherPOL(this.validatorContract, toWei('4600'), this.bob) + const validatorAlice = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.alice)) + await validatorAlice.withdrawRewardsPOL() + }) + + it('Bob must call getLiquidRewards', async function () { + await this.validatorContract.getLiquidRewards(this.bob) + }) + }) + }) + + describe('transfer', function () { + describe('when Alice has no rewards', function () { + deployAliceAndBob() + + let initialSharesBalance + + before('Alice purchases voucher', async function () { + await buyVoucherPOL(this.validatorContract, toWei('100'), this.alice) + initialSharesBalance = await this.validatorContract.balanceOf(this.alice) + }) + + it('must Transfer shares', async function () { + const validatorAlice = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.alice)) + await validatorAlice.transferPOL(this.bob, initialSharesBalance) + }) + + it('Alice must have 0 shares', async function () { + assertBigNumberEquality(await this.validatorContract.balanceOf(this.alice), '0') + }) + + it("Bob must have Alice's shares", async function () { + assertBigNumberEquality(await this.validatorContract.balanceOf(this.bob), initialSharesBalance) + }) + }) + + describe('when Alice and Bob have unclaimed rewards', function () { + deployAliceAndBob() + + let initialAliceSharesBalance + let initialBobSharesBalance + + let initialAlicePolBalance + let initialBobPolBalance + + let initialAliceStakeBalance + let initialBobStakeBalance + + before('Alice and Bob purchases voucher, checkpoint is commited', async function () { + await buyVoucherPOL(this.validatorContract, ValidatorDefaultStake, this.alice) + await buyVoucherPOL(this.validatorContract, ValidatorDefaultStake, this.bob) + + initialAliceSharesBalance = await this.validatorContract.balanceOf(this.alice) + initialBobSharesBalance = await this.validatorContract.balanceOf(this.bob) + + initialAlicePolBalance = await this.polToken.balanceOf(this.alice) + initialBobPolBalance = await this.polToken.balanceOf(this.bob) + + initialAliceStakeBalance = await this.stakeToken.balanceOf(this.alice) + initialBobStakeBalance = await this.stakeToken.balanceOf(this.bob) + + await checkPoint([this.validatorUser], this.rootChainOwner, this.stakeManager) + }) + + it('must Transfer shares', async function () { + const validatorAlice = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.alice)) + this.receipt = await (await validatorAlice.transferPOL(this.bob, initialAliceSharesBalance)).wait() + }) + + it('must emit DelegatorClaimedRewards for Alice', async function () { + assertInTransaction(this.receipt, StakingInfo, 'DelegatorClaimedRewards', { + validatorId: this.validatorId, + user: this.alice, + rewards: toWei('3000') + }) + }) + + it('Alice must claim 3000 pol', async function () { + assertBigNumberEquality( + await this.polToken.balanceOf(this.alice), + initialAlicePolBalance.add(toWei('3000')) + ) + }) + + it('Alice must have unchanged matic', async function () { + assertBigNumberEquality( + await this.stakeToken.balanceOf(this.alice), + initialAliceStakeBalance + ) + }) + + it('Alice must have 0 liquid rewards', async function () { + assertBigNumberEquality(await this.validatorContract.getLiquidRewards(this.alice), '0') + }) + + it('Alice must have 0 shares', async function () { + assertBigNumberEquality(await this.validatorContract.balanceOf(this.alice), '0') + }) + + it("Bob must have Alice's shares", async function () { + assertBigNumberEquality( + await this.validatorContract.balanceOf(this.bob), + initialBobSharesBalance.add(initialAliceSharesBalance) + ) + }) + + it('must emit DelegatorClaimedRewards for Bob', async function () { + assertInTransaction(this.receipt, StakingInfo, 'DelegatorClaimedRewards', { + validatorId: this.validatorId, + user: this.bob, + rewards: toWei('3000') + }) + }) + + it('Bob must claim 3000 pol', async function () { + assertBigNumberEquality( + await this.polToken.balanceOf(this.bob), + initialBobPolBalance.add(toWei('3000')) + ) + }) + + it('Bob must have unchanged pol', async function () { + assertBigNumberEquality( + await this.stakeToken.balanceOf(this.bob), + initialBobStakeBalance + ) + }) + + it('Bob must have 0 liquid rewards', async function () { + assertBigNumberEquality(await this.validatorContract.getLiquidRewards(this.bob), '0') + }) + }) + + describe('when transfer to 0x0 address', function () { + deployAliceAndBob() + + let initialAliceSharesBalance + + before('Alice purchases voucher', async function () { + initialAliceSharesBalance = await this.validatorContract.balanceOf(this.alice) + + await buyVoucherPOL(this.validatorContract, ValidatorDefaultStake, this.alice) + }) + + it('reverts', async function () { + const validatorAlice = this.validatorContract.connect(this.validatorContract.provider.getSigner(this.alice)) + await expectRevert.unspecified( + validatorAlice.transferPOL(ZeroAddr, initialAliceSharesBalance) + ) + }) + }) + }) +}) + +function shouldHaveCorrectStakes({ user, userTotalStaked, totalStaked }) { + it('must have correct total staked', async function () { + const result = await this.validatorContract.amountStaked(user || this.user) + assertBigNumberEquality(result, userTotalStaked) + }) + + it('validator state must have correct amount', async function () { + assertBigNumberEquality(await this.stakeManager.currentValidatorSetTotalStake(), totalStaked) + }) +} + +function shouldBuyShares({ shares, voucherValueExpected, totalStaked }) { + it('ValidatorShare must mint correct amount of shares', async function () { + assertInTransaction(this.receipt, ValidatorShare, 'Transfer', { + from: ZeroAddr, + to: this.user, + value: shares.toString() + }) + }) + + it('must emit ShareMinted', async function () { + assertInTransaction(this.receipt, StakingInfo, 'ShareMinted', { + validatorId: this.validatorId, + user: this.user, + amount: voucherValueExpected.toString(), + tokens: shares.toString() + }) + }) + + it('must emit StakeUpdate', async function () { + assertInTransaction(this.receipt, StakingInfo, 'StakeUpdate', { + validatorId: this.validatorId, + newAmount: totalStaked.toString() + }) + }) +} + +function shouldWithdrawReward({ initialBalance, validatorId, user, reward, checkBalance = true }) { + if (reward > 0) { + it('must emit Transfer', async function () { + assertInTransaction(this.receipt, TestToken, 'Transfer', { + from: this.stakeManager.address, + to: user || this.user, + value: reward + }) + }) + + it('must emit DelegatorClaimedRewards', async function () { + assertInTransaction(this.receipt, StakingInfo, 'DelegatorClaimedRewards', { + validatorId: validatorId.toString(), + user: user || this.user, + rewards: reward.toString() + }) + }) + } + + if (checkBalance) { + it('must have updated balance', async function () { + const balance = await this.polToken.balanceOf(user || this.user) + assertBigNumberEquality(balance, new BN(initialBalance).add(new BN(reward))) + }) + } + + it('must have liquid rewards == 0', async function () { + let rewards = await this.validatorContract.getLiquidRewards(user || this.user) + assertBigNumberEquality('0', rewards) + }) + + it('must have correct initialRewardPerShare', async function () { + const currentRewardPerShare = await this.validatorContract.rewardPerShare() + const userRewardPerShare = await this.validatorContract.initalRewardPerShare(user || this.user) + assertBigNumberEquality(currentRewardPerShare, userRewardPerShare) + }) +} diff --git a/test/units/staking/deployment.js b/test/units/staking/deployment.js index aed2f4e9..9b28735b 100644 --- a/test/units/staking/deployment.js +++ b/test/units/staking/deployment.js @@ -34,9 +34,10 @@ export const walletAmounts = { } } -export async function freshDeploy() { - let contracts = await deployer.deployStakeManager(wallets) +export async function freshDeploy(pol = false) { + let contracts = await deployer.deployStakeManager(wallets, pol) this.stakeToken = contracts.stakeToken + this.polToken = contracts.polToken this.stakeManager = contracts.stakeManager this.nftContract = contracts.stakingNFT this.rootChainOwner = contracts.rootChainOwner @@ -56,10 +57,11 @@ export async function freshDeploy() { for (const walletAddr in walletAmounts) { await this.stakeToken.mint(walletAddr, walletAmounts[walletAddr].initialBalance) + if (pol) { + await this.polToken.mint(walletAddr, walletAmounts[walletAddr].initialBalance) + } } - await this.stakeToken.mint(this.stakeManager.address, web3.utils.toWei('10000000')) - this.defaultHeimdallFee = new BN(web3.utils.toWei('1')) } @@ -70,32 +72,51 @@ export async function approveAndStake({ acceptDelegation = false, heimdallFee, noMinting = false, - signer + signer, + pol = false }) { const fee = heimdallFee || this.defaultHeimdallFee const mintAmount = new BN(approveAmount || stakeAmount).add(new BN(fee)) + let token + if (pol) { + token = this.polToken + } else { + token = this.stakeToken + } + if (noMinting) { // check if allowance covers fee - const balance = await this.stakeToken.balanceOf(wallet.getAddressString()) + const balance = await token.balanceOf(wallet.getAddressString()) if (balance.lt(mintAmount.toString())) { // mint more - await this.stakeToken.mint(wallet.getAddressString(), mintAmount.sub(balance).toString()) + await token.mint(wallet.getAddressString(), mintAmount.sub(balance).toString()) } } else { - await this.stakeToken.mint(wallet.getAddressString(), mintAmount.toString()) + await token.mint(wallet.getAddressString(), mintAmount.toString()) } - const stakeTokenWallet = this.stakeToken.connect(this.stakeToken.provider.getSigner(wallet.getAddressString())) - await stakeTokenWallet.approve(this.stakeManager.address, mintAmount.toString()) + const tokenWallet = token.connect(token.provider.getSigner(wallet.getAddressString())) + await tokenWallet.approve(this.stakeManager.address, mintAmount.toString()) const stakeManagerWallet = this.stakeManager.connect(this.stakeManager.provider.getSigner(wallet.getAddressString())) - await stakeManagerWallet.stakeFor( - wallet.getAddressString(), - stakeAmount.toString(), - fee.toString(), - acceptDelegation, - signer || wallet.getPublicKeyString() - ) + + if (pol) { + await stakeManagerWallet.stakeForPOL( + wallet.getAddressString(), + stakeAmount.toString(), + fee.toString(), + acceptDelegation, + signer || wallet.getPublicKeyString() + ) + } else { + await stakeManagerWallet.stakeFor( + wallet.getAddressString(), + stakeAmount.toString(), + fee.toString(), + acceptDelegation, + signer || wallet.getPublicKeyString() + ) + } } diff --git a/test/units/staking/permitHelper.js b/test/units/staking/permitHelper.js new file mode 100644 index 00000000..83e11f51 --- /dev/null +++ b/test/units/staking/permitHelper.js @@ -0,0 +1,66 @@ +import { keccak256 } from 'ethereumjs-util' +import encode from 'ethereumjs-abi' + +const PERMIT_TYPEHASH = keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)') // 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9 + +export function getStructHash({ owner, spender, value, nonce, deadline }) { + return keccak256( + encode( + ['bytes32', 'address', 'address', 'uint256', 'uint256', 'uint256'], + [PERMIT_TYPEHASH, owner, spender, value, nonce, deadline] + ) + ) +} + +export function getTypedDataHash(DOMAIN_SEPARATOR, permitData) { + return keccak256(encode(['bytes', 'bytes32', 'bytes32'], ['\x19\x01', DOMAIN_SEPARATOR, getStructHash(permitData)])) +} + +const cache = new Map() + +export async function getPermitDigest(owner, spender, value, token, deadline) { + let { name, version } = cache.get(token.address) || {} + if (!name || !version) { + ;[name, version] = await Promise.all([token.name(), token.version()]) + cache.set(token.address, { name, version }) + } + return [ + { + name: 'POL', + version: '1.1.0', + chainId: (await token.provider._network).chainId, + verifyingContract: token.address + }, + { + Permit: [ + { + name: 'owner', + type: 'address' + }, + { + name: 'spender', + type: 'address' + }, + { + name: 'value', + type: 'uint256' + }, + { + name: 'nonce', + type: 'uint256' + }, + { + name: 'deadline', + type: 'uint256' + } + ] + }, + { + owner, + spender, + value, + nonce: await token.nonces(owner), + deadline + } + ] +} diff --git a/test/units/staking/stakeManager/StakeManager.Staking.js b/test/units/staking/stakeManager/StakeManager.Staking.js index 8d200656..da71b106 100644 --- a/test/units/staking/stakeManager/StakeManager.Staking.js +++ b/test/units/staking/stakeManager/StakeManager.Staking.js @@ -42,7 +42,7 @@ export function doUnstake(wallet) { export function prepareForTest(dynastyValue, validatorThreshold) { return async function () { - await freshDeploy.call(this) + await freshDeploy.call(this, true) await this.governance.update( this.stakeManager.address, @@ -208,7 +208,9 @@ describe('stake', function () { } describe('double stake', async function () { - before(freshDeploy) + before(async function() { + await freshDeploy.call(this, true) + }) describe('when stakes first time', function () { const amounts = walletAmounts[wallets[1].getAddressString()] @@ -232,7 +234,9 @@ describe('stake', function () { }) describe('stake and restake following by another stake', function () { - before(freshDeploy) + before(async function() { + await freshDeploy.call(this, true) + }) const amounts = walletAmounts[wallets[2].getAddressString()] before('Stake', doStake(wallets[2])) @@ -259,12 +263,12 @@ describe('stake', function () { it('when auction is active', async function () { let auctionBid = web3.utils.toWei('10000') const auctionUser = wallets[4].getAddressString() - await this.stakeToken.mint(auctionUser, auctionBid) + await this.polToken.mint(auctionUser, auctionBid) - const stakeTokenUser = this.stakeToken.connect(this.stakeToken.provider.getSigner(auctionUser)) + const polTokenUser = this.polToken.connect(this.polToken.provider.getSigner(auctionUser)) const stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(auctionUser)) - await stakeTokenUser.approve(this.stakeManager.address, auctionBid) + await polTokenUser.approve(this.stakeManager.address, auctionBid) const validatorId = await this.stakeManager.getValidatorId(wallets[2].getChecksumAddressString()) await stakeManagerUser.startAuction(validatorId, auctionBid, false, wallets[4].getPublicKeyString()) testRestake( @@ -304,7 +308,9 @@ describe('stake', function () { }) describe('consecutive stakes', function () { - before(freshDeploy) + before(async function() { + await freshDeploy.call(this, true) + }) it('validatorId must increment 1 by 1', async function () { const _wallets = [wallets[1], wallets[2], wallets[3]] @@ -322,7 +328,9 @@ describe('stake', function () { }) describe('stake with heimdall fee', function () { - before(freshDeploy) + before(async function() { + await freshDeploy.call(this, true) + }) testStake( wallets[0].getChecksumAddressString(), @@ -338,7 +346,9 @@ describe('stake', function () { const AliceWallet = wallets[1] const newSigner = wallets[2].getPublicKeyString() - before(freshDeploy) + before(async function() { + await freshDeploy.call(this, true) + }) before(doStake(AliceWallet)) before('Change signer', async function () { const signerUpdateLimit = await this.stakeManager.signerUpdateLimit() @@ -365,7 +375,9 @@ describe('unstake', function () { const AliceNewWallet = wallets[2] let stakeManagerAlice - before(freshDeploy) + before(async function() { + await freshDeploy.call(this, true) + }) before(doStake(AliceWallet)) before(doStake(BobWallet)) before('Change signer', async function () { @@ -398,6 +410,58 @@ describe('unstake', function () { }) }) + describe('forceUnstake cannot only unstake active validators', function () { + const AliceWallet = wallets[1] + const others = [wallets[2], wallets[3]] + + before(async function() { + await freshDeploy.call(this, true) + }) + before(doStake(AliceWallet)) + before(doStake(others[0])) + before(doStake(others[1])) + before('Alice is forceUnstaked', async function () { + this.validatorId = await this.stakeManager.getValidatorId(AliceWallet.getAddressString()) + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('forceUnstake', [this.validatorId]) + ) + this.lastSyncedEpoch = await this.stakeManager.currentEpoch() + await checkPoint(others, this.rootChainOwner, this.stakeManager) + }) + it('Alice is unstaked', async function () { + const { deactivationEpoch } = await this.stakeManager.validators(this.validatorId) + assertBigNumberEquality(deactivationEpoch, this.lastSyncedEpoch) + }) + it('Alice cannot be forceUnstaked again', async function () { + await expectRevert(this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('forceUnstake', [this.validatorId]) + ), 'Update failed') + }) + it('Alice cannot be forceUnstaked after claiming', async function () { + const endEpoch = this.lastSyncedEpoch.add(await this.stakeManager.WITHDRAWAL_DELAY()) + // mock for i ... range(delay) checkPoint() + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('setCurrentEpoch', [endEpoch + 1]) + ) + + await this.stakeManager + .connect(this.stakeManager.provider.getSigner(AliceWallet.getAddressString())) + .unstakeClaim(this.validatorId) + await checkPoint(others, this.rootChainOwner, this.stakeManager) + + await expectRevert( + this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('forceUnstake', [this.validatorId]) + ), + 'Update failed' + ) + }) + }) + describe('when user unstakes right after stake', async function () { const user = wallets[2].getChecksumAddressString() const amounts = walletAmounts[wallets[2].getAddressString()] @@ -546,7 +610,9 @@ describe('unstake', function () { }) describe('reverts', function () { - beforeEach('Fresh Deploy', freshDeploy) + beforeEach('Fresh Deploy', async function() { + await freshDeploy.call(this, true) + }) const user = wallets[2].getChecksumAddressString() let stakeManagerUser @@ -575,12 +641,12 @@ describe('unstake', function () { it('when unstakes during auction', async function () { const amount = web3.utils.toWei('1200').toString() const auctionUser = wallets[4].getAddressString() - await this.stakeToken.mint(auctionUser, amount) + await this.polToken.mint(auctionUser, amount) - const stakeTokenAuctionUser = this.stakeToken.connect(this.stakeToken.provider.getSigner(auctionUser)) + const polTokenAuctionUser = this.polToken.connect(this.polToken.provider.getSigner(auctionUser)) const stakeManagerAuctionUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(auctionUser)) - await stakeTokenAuctionUser.approve(this.stakeManager.address, amount) + await polTokenAuctionUser.approve(this.stakeManager.address, amount) const validatorId = await this.stakeManager.getValidatorId(user) await stakeManagerAuctionUser.startAuction(validatorId, amount, false, wallets[4].getPublicKeyString()) await expectRevert.unspecified(stakeManagerUser.unstake(validatorId)) diff --git a/test/units/staking/stakeManager/StakeManager.StakingPol.js b/test/units/staking/stakeManager/StakeManager.StakingPol.js new file mode 100644 index 00000000..e9f96b97 --- /dev/null +++ b/test/units/staking/stakeManager/StakeManager.StakingPol.js @@ -0,0 +1,1066 @@ +import { StakingInfo, TestToken, PolygonMigration, ValidatorShare } from '../../../helpers/artifacts.js' + +import { checkPoint, assertBigNumberEquality, assertBigNumbergt } from '../../../helpers/utils.js' +import testHelpers from '@openzeppelin/test-helpers' +const expectRevert = testHelpers.expectRevert +const BN = testHelpers.BN +import { wallets, walletAmounts, freshDeploy, approveAndStake } from '../deployment.js' +import { assert, expect } from 'chai' +import * as utils from '../../../helpers/utils.js' + +export function doStake( + wallet, + { approveAmount, stakeAmount, noMinting = false, acceptDelegation = false, signer } = {} +) { + return async function () { + let user = wallet.getAddressString() + + let _approveAmount = approveAmount || walletAmounts[user].amount + let _stakeAmount = stakeAmount || walletAmounts[user].stakeAmount + + await approveAndStake.call(this, { + wallet, + stakeAmount: _stakeAmount, + approveAmount: _approveAmount, + acceptDelegation, + noMinting, + signer, + pol: true + }) + } +} + +export function doUnstake(wallet) { + return async function () { + let user = wallet.getAddressString() + + const stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) + + const validatorId = await this.stakeManager.getValidatorId(user) + await stakeManagerUser.unstakePOL(validatorId) + } +} + +export function prepareForTest(dynastyValue, validatorThreshold) { + return async function () { + await freshDeploy.call(this, true) + + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('updateValidatorThreshold', [validatorThreshold]) + ) + + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('updateDynastyValue', [dynastyValue]) + ) + } +} + +describe('migrate matic', function (){ + describe('initializePOL', function(){ + before('Setup migration scenario', async function() { + await freshDeploy.call(this) + }) + + describe('must revert with pol functions', function(){ + let stakeTokenUser, user, stakeManagerUser, amount, userPubkey + before('', async function(){ + userPubkey = wallets[1].getPublicKeyString(), + user = wallets[1].getChecksumAddressString() + amount = web3.utils.toWei('10') + + stakeTokenUser = this.stakeToken.connect(this.stakeToken.provider.getSigner(user)) + stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) + }) + + it('on stake token', async function () { + await stakeTokenUser.approve(this.stakeManager.address, amount.toString()) + await expect(stakeManagerUser.stakeForPOL(user, amount.toString(), web3.utils.toWei('1'), false, userPubkey)) + .to.be.rejectedWith('') + }) + }) + + describe('run initializePOL', function(){ + let oldStakeToken, newStakeToken, migrationAmount + before('gov update', async function(){ + migrationAmount = web3.utils.toWei('20000000') + + oldStakeToken = await TestToken.deploy('MATIC', 'MAT') + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('setStakingToken', [oldStakeToken.address]) + ) + await oldStakeToken.mint(this.stakeManager.address, migrationAmount) + + newStakeToken = await TestToken.deploy('POL', 'POL') + + this.migration = await PolygonMigration.deploy(oldStakeToken.address, newStakeToken.address) + + await oldStakeToken.mint(this.migration.address, web3.utils.toWei('50000000')) + await newStakeToken.mint(this.migration.address, web3.utils.toWei('50000000')) + }) + + it('must initializePOL', async function () { + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('initializePOL', [newStakeToken.address, this.migration.address]) + ) + }) + + it('stakemanager must have correct pol balance', async function () { + let polBalance = await oldStakeToken.balanceOf(this.stakeManager.address) + assertBigNumberEquality(BN(0), polBalance) + }) + + it('stakemanager must have correct stake balance', async function () { + let stakeBalance = await newStakeToken.balanceOf(this.stakeManager.address) + assertBigNumberEquality(migrationAmount, stakeBalance) + }) + }) + }) + + describe('successful migration', function(){ + const migrationAmount = new BN('100000000000000') + const initalPolAmount = new BN('100000000000000') + const initalStakeAmount = new BN(0) + + before('Setup migration scenario', async function() { + await freshDeploy.call(this) + + this.stakeToken = await TestToken.deploy('POL', 'POL') + this.polToken = await TestToken.deploy('MATIC', 'MATIC') + + this.migration = await PolygonMigration.deploy(this.polToken.address, this.stakeToken.address) + + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('setStakingToken', [this.polToken.address]) + ) + + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('initializePOL', [this.stakeToken.address, this.migration.address]) + ) + + await this.stakeToken.mint(this.stakeManager.address, initalStakeAmount.toString()) + await this.polToken.mint(this.stakeManager.address, initalPolAmount.toString()) + + await this.stakeToken.mint(this.migration.address, web3.utils.toWei('50000000')) + await this.polToken.mint(this.migration.address, web3.utils.toWei('50000000')) + }) + + it('must revert with too many token', async function () { + const bigAmount = initalPolAmount.mul(new BN('20')) + await expectRevert( this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('convertMaticToPOL', [bigAmount.toString()]) + ), 'Update failed') + //'Insufficient MATIC balance' + }) + + it('must migrate', async function () { + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('convertMaticToPOL', [migrationAmount.toString()]) + ) + }) + it('must have correct pol balance', async function(){ + const polBalance = await this.polToken.balanceOf(this.stakeManager.address) + assertBigNumberEquality(polBalance, initalPolAmount.sub(migrationAmount)) + }) + + it('must have correct stake balance', async function(){ + const stakeBalance = await this.stakeToken.balanceOf(this.stakeManager.address) + assertBigNumberEquality(stakeBalance, initalStakeAmount.add(migrationAmount)) + }) + }) +}) + + +describe('stake POL', function () { + function testStakeRevert(user, userPubkey, amount, stakeAmount, unspecified = false) { + let stakeManagerUser, polTokenUser + + before('Approve', async function () { + this.initialBalance = await this.stakeManager.totalStakedFor(user) + + polTokenUser = this.polToken.connect(this.polToken.provider.getSigner(user)) + stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) + + await polTokenUser.approve(this.stakeManager.address, new BN(amount).add(this.defaultHeimdallFee).toString()) + }) + + it('must revert', async function () { + if (unspecified) { + await expectRevert( + stakeManagerUser.stakeForPOL(user, stakeAmount, this.defaultHeimdallFee.toString(), false, userPubkey), + 'no more slots' + ) + } else { + await expectRevert( + stakeManagerUser.stakeForPOL(user, stakeAmount, this.defaultHeimdallFee.toString(), false, userPubkey), + 'Invalid signer' + ) + } + }) + + it('must have unchanged staked balance', async function () { + const stakedFor = await this.stakeManager.totalStakedFor(user) + assertBigNumberEquality(stakedFor, this.initialBalance) + }) + } + + function testStake(user, userPubkey, amount, stakeAmount, validatorId, fee) { + let stakeManagerUser, polTokenUser + before('Approve', async function () { + this.user = user + this.fee = new BN(fee || this.defaultHeimdallFee) + + polTokenUser = this.polToken.connect(this.polToken.provider.getSigner(user)) + stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) + + await polTokenUser.approve(this.stakeManager.address, new BN(amount).add(this.fee).toString()) + }) + + it('must stake', async function () { + this.receipt = await ( + await stakeManagerUser.stakeForPOL(user, stakeAmount.toString(), this.fee.toString(), false, userPubkey) + ).wait() + }) + + it('must emit Staked', async function () { + utils.assertInTransaction(this.receipt, StakingInfo, 'Staked', { + signerPubkey: userPubkey, + signer: user, + amount: stakeAmount + }) + }) + + if (fee) { + it('must emit TopUpFee', async function () { + utils.assertInTransaction(this.receipt, StakingInfo, 'TopUpFee', { + user: user, + fee: this.fee.toString() + }) + }) + } + + it(`must have correct staked amount`, async function () { + const stakedFor = await this.stakeManager.totalStakedFor(user) + assertBigNumberEquality(stakedFor, stakeAmount) + }) + + it('must have correct total staked balance', async function () { + const stake = await this.stakeManager.currentValidatorSetTotalStake() + assertBigNumberEquality(stake, stakeAmount) + }) + + it(`must have validatorId == ${validatorId}`, async function () { + const _validatorId = await this.stakeManager.getValidatorId(user) + _validatorId.toString().should.be.equal(validatorId.toString()) + }) + + it('must have valid validatorId', async function () { + const validatorId = await this.stakeManager.getValidatorId(user) + const value = await this.stakeManager.isValidator(validatorId.toString()) + assert.isTrue(value) + }) + + it('must pay out rewards correctly', async function () { + const validatorId = await this.stakeManager.getValidatorId(user) + const reward = await this.stakeManager.validatorReward(validatorId) + const balanceBefore = await this.polToken.balanceOf(user) + assertBigNumberEquality(reward, new BN(0)) + + await checkPoint(wallets, this.rootChainOwner, this.stakeManager) + await checkPoint(wallets, this.rootChainOwner, this.stakeManager) + const newReward = await this.stakeManager.validatorReward(validatorId) + assertBigNumbergt(newReward, reward) + await stakeManagerUser.withdrawRewardsPOL(validatorId) + const balanceAfter = await this.polToken.balanceOf(user) + assertBigNumberEquality(balanceAfter.sub(balanceBefore), newReward) + + await checkPoint(wallets, this.rootChainOwner, this.stakeManager) + await checkPoint(wallets, this.rootChainOwner, this.stakeManager) + const newReward2 = await this.stakeManager.validatorReward(validatorId) + await stakeManagerUser.withdrawRewardsPOL(validatorId) + const balanceAfter2 = await this.polToken.balanceOf(user) + assertBigNumberEquality(balanceAfter2.sub(balanceAfter), newReward2) + assertBigNumberEquality(newReward2, newReward) + }) + } + + function testRestake(user, amount, stakeAmount, restakeAmount, totalStaked) { + let stakeManagerUser, polTokenUser + + before('Approve', async function () { + this.user = user + polTokenUser = this.polToken.connect(this.polToken.provider.getSigner(user)) + stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) + + await polTokenUser.approve(this.stakeManager.address, amount.toString()) + }) + + it('must restake', async function () { + const validatorId = await this.stakeManager.getValidatorId(this.user) + this.receipt = await (await stakeManagerUser.restakePOL(validatorId, restakeAmount, false)).wait() + }) + + it('must emit StakeUpdate', async function () { + const validatorId = await this.stakeManager.getValidatorId(this.user) + await utils.assertInTransaction(this.receipt, StakingInfo, 'StakeUpdate', { + validatorId: validatorId.toString() + }) + }) + + it('must emit Restaked', async function () { + const validatorId = await this.stakeManager.getValidatorId(this.user) + await utils.assertInTransaction(this.receipt, StakingInfo, 'Restaked', { + validatorId, + amount: stakeAmount.toString(), + total: totalStaked.toString() + }) + }) + + it(`must have correct total staked amount`, async function () { + const stakedFor = await this.stakeManager.totalStakedFor(user) + assertBigNumberEquality(stakedFor, stakeAmount) + }) + } + + describe('double stake', async function () { + before('deploy', async function() { + await freshDeploy.call(this, true) + }) + + describe('when stakes first time', function () { + const amounts = walletAmounts[wallets[1].getAddressString()] + testStake( + wallets[1].getChecksumAddressString(), + wallets[1].getPublicKeyString(), + amounts.amount, + amounts.stakeAmount, + 1 + ) + }) + + describe('when stakes again', function () { + testStakeRevert( + wallets[1].getChecksumAddressString(), + wallets[1].getPublicKeyString(), + web3.utils.toWei('200'), + web3.utils.toWei('200') + ) + }) + }) + + describe('stake and restake following by another stake', function () { + before('deploy', async function() { + await freshDeploy.call(this, true) + }) + + const amounts = walletAmounts[wallets[2].getAddressString()] + before('Stake', doStake(wallets[2])) + + describe('when restakes', function () { + testRestake( + wallets[2].getChecksumAddressString(), + amounts.restakeAmonut, + amounts.amount, + amounts.restakeAmonut, + amounts.amount + ) + }) + + describe('when stakes again', function () { + testStakeRevert( + wallets[2].getChecksumAddressString(), + wallets[2].getPublicKeyString(), + web3.utils.toWei('250'), + web3.utils.toWei('150') + ) + }) + describe('when reStakes while on going auction', function () { + it('when auction is active', async function () { + let auctionBid = web3.utils.toWei('10000') + const auctionUser = wallets[4].getAddressString() + await this.polToken.mint(auctionUser, auctionBid) + + const polTokenUser = this.polToken.connect(this.polToken.provider.getSigner(auctionUser)) + const stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(auctionUser)) + + await polTokenUser.approve(this.stakeManager.address, auctionBid) + const validatorId = await this.stakeManager.getValidatorId(wallets[2].getChecksumAddressString()) + await stakeManagerUser.startAuction(validatorId, auctionBid, false, auctionUser) + testRestake( + wallets[2].getChecksumAddressString(), + amounts.restakeAmonut, + amounts.amount, + amounts.restakeAmonut, + amounts.amount + ) + }) + }) + }) + + describe('stake beyond validator threshold', async function () { + before(prepareForTest(2, 1)) + + describe('when user stakes', function () { + const amounts = walletAmounts[wallets[3].getAddressString()] + testStake( + wallets[3].getChecksumAddressString(), + wallets[3].getPublicKeyString(), + amounts.amount, + amounts.stakeAmount, + 1 + ) + }) + + describe('when other user stakes beyond validator threshold', function () { + testStakeRevert( + wallets[4].getChecksumAddressString(), + wallets[4].getPublicKeyString(), + web3.utils.toWei('100'), + web3.utils.toWei('100'), + true + ) + }) + }) + + describe('consecutive stakes', function () { + before('deploy', async function() { + await freshDeploy.call(this, true) + }) + + it('validatorId must increment 1 by 1', async function () { + const _wallets = [wallets[1], wallets[2], wallets[3]] + let expectedValidatorId = 1 + for (const wallet of _wallets) { + await doStake(wallet, { approveAmount: web3.utils.toWei('100'), stakeAmount: web3.utils.toWei('100') }).call( + this + ) + + const validatorId = await this.stakeManager.getValidatorId(wallet.getAddressString()) + assertBigNumberEquality(expectedValidatorId, validatorId) + expectedValidatorId++ + } + }) + }) + + describe('stake with heimdall fee', function () { + before('deploy', async function() { + await freshDeploy.call(this, true) + }) + + testStake( + wallets[0].getChecksumAddressString(), + wallets[0].getPublicKeyString(), + web3.utils.toWei('200'), + web3.utils.toWei('150'), + 1, + web3.utils.toWei('50') + ) + }) + + describe('when Alice stakes, change signer and stakes with old signer', function () { + const AliceWallet = wallets[1] + const newSigner = wallets[2].getPublicKeyString() + + before('deploy', async function() { + await freshDeploy.call(this, true) + }) + before(doStake(AliceWallet)) + before('Change signer', async function () { + const signerUpdateLimit = await this.stakeManager.signerUpdateLimit() + await this.stakeManager.advanceEpoch(signerUpdateLimit) + + this.validatorId = await this.stakeManager.getValidatorId(AliceWallet.getAddressString()) + + const stakeManagerAlice = this.stakeManager.connect( + this.stakeManager.provider.getSigner(AliceWallet.getAddressString()) + ) + await stakeManagerAlice.updateSigner(this.validatorId, newSigner) + }) + + it('reverts', async function () { + await expectRevert(doStake(wallets[3], { signer: AliceWallet.getPublicKeyString() }).call(this), 'Invalid signer') + }) + }) +}) + +describe('unstake POL', function () { + describe('when Alice unstakes and update the signer', function () { + const AliceWallet = wallets[1] + const BobWallet = wallets[3] + const AliceNewWallet = wallets[2] + let stakeManagerAlice + + before('deploy', async function() { + await freshDeploy.call(this, true) + }) + before(doStake(AliceWallet)) + before(doStake(BobWallet)) + before('Change signer', async function () { + const signerUpdateLimit = await this.stakeManager.signerUpdateLimit() + await this.stakeManager.advanceEpoch(signerUpdateLimit) + + this.validatorId = await this.stakeManager.getValidatorId(AliceWallet.getAddressString()) + + stakeManagerAlice = this.stakeManager.connect( + this.stakeManager.provider.getSigner(AliceWallet.getAddressString()) + ) + }) + + it('Alice should unstake', async function () { + this.receipt = await stakeManagerAlice.unstakePOL(this.validatorId) + }) + + it("Signers list should have only Bob's signer", async function () { + assert((await this.stakeManager.signers(0)) == BobWallet.getChecksumAddressString(), 'no Bob signer!') + await expect(this.stakeManager.signers(1)) + }) + + it('Alice should update signer', async function () { + await stakeManagerAlice.updateSigner(this.validatorId, AliceNewWallet.getPublicKeyString()) + }) + + it("Signers list should haveonly Bob's signer", async function () { + assert((await this.stakeManager.signers(0)) == BobWallet.getChecksumAddressString(), 'no Bob signer!') + await expect(this.stakeManager.signers(1)) + }) + }) + + describe('when user unstakes right after stake', async function () { + const user = wallets[2].getChecksumAddressString() + const amounts = walletAmounts[wallets[2].getAddressString()] + let stakeManagerUser + + before('Fresh deploy', prepareForTest(2, 3)) + before(doStake(wallets[2])) + before(async function () { + this.validatorId = await this.stakeManager.getValidatorId(user) + + const reward = await this.stakeManager.validatorReward(this.validatorId) + this.reward = reward + this.afterStakeBalance = await this.polToken.balanceOf(user) + + stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) + }) + + it('must unstake', async function () { + this.receipt = await (await stakeManagerUser.unstakePOL(this.validatorId)).wait() + }) + + it('must emit UnstakeInit', async function () { + await utils.assertInTransaction(this.receipt, StakingInfo, 'UnstakeInit', { + amount: amounts.stakeAmount, + validatorId: this.validatorId, + user + }) + }) + + it('must emit ClaimRewards', async function () { + await utils.assertInTransaction(this.receipt, StakingInfo, 'ClaimRewards', { + validatorId: this.validatorId, + amount: '0', + totalAmount: '0' + }) + }) + + it('must emit Transfer', async function () { + await utils.assertInTransaction(this.receipt, TestToken, 'Transfer', { + value: this.reward, + to: user + }) + }) + + it('must have increased balance by reward', async function () { + const balance = await this.polToken.balanceOf(user) + assertBigNumberEquality(balance, this.afterStakeBalance.add(this.reward)) + }) + }) + + describe('when user unstakes after 2 epochs', async function () { + before('Fresh deploy', prepareForTest(2, 3)) + + const user = wallets[3].getChecksumAddressString() + const amounts = walletAmounts[wallets[3].getAddressString()] + const w = [wallets[2], wallets[3]] + let stakeManagerUser + + before(doStake(wallets[2])) + before(doStake(wallets[3], { acceptDelegation: true })) + before(async function () { + this.validatorId = await this.stakeManager.getValidatorId(user) + + const polToken4 = this.polToken.connect(this.polToken.provider.getSigner(wallets[4].getAddressString())) + stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) + + // delegate tokens to validator + const validatorShareAddr = await this.stakeManager.getValidatorContract(this.validatorId) + await polToken4.approve(this.stakeManager.address, web3.utils.toWei('100')) + const validator = await ValidatorShare.attach(validatorShareAddr) + const validator4 = validator.connect(validator.provider.getSigner(wallets[4].getAddressString())) + await validator4.buyVoucherPOL(web3.utils.toWei('100'), 0) + + this.afterStakeBalance = await this.polToken.balanceOf(user) + }) + + before(async function () { + await checkPoint(w, this.rootChainOwner, this.stakeManager) + await checkPoint(w, this.rootChainOwner, this.stakeManager) + + this.reward = await this.stakeManager.validatorReward(this.validatorId) + this.delegatorReward = await this.stakeManager.delegatorsReward(this.validatorId) + }) + + it('must unstake', async function () { + const validatorId = await this.stakeManager.getValidatorId(user) + this.receipt = await (await stakeManagerUser.unstakePOL(validatorId)).wait() + }) + + it('must emit UnstakeInit', async function () { + const validatorId = await this.stakeManager.getValidatorId(user) + await utils.assertInTransaction(this.receipt, StakingInfo, 'UnstakeInit', { + amount: amounts.amount, + validatorId, + user + }) + }) + + it('must emit ClaimRewards', async function () { + await utils.assertInTransaction(this.receipt, StakingInfo, 'ClaimRewards', { + validatorId: this.validatorId, + amount: this.reward, + totalAmount: this.reward + }) + }) + + it('must emit Transfer', async function () { + await utils.assertInTransaction(this.receipt, TestToken, 'Transfer', { + value: this.reward, + to: user + }) + }) + + it('must have increased balance by reward', async function () { + const balance = await this.polToken.balanceOf(user) + assertBigNumberEquality(balance, this.afterStakeBalance.add(this.reward)) + }) + + it('should not distribute additional staking or delegation rewards after unstaking', async function () { + const validatorId = await this.stakeManager.getValidatorId(user) + + let validatorRewardsBefore = await this.stakeManager.validatorReward(validatorId) + let delegationRewardsBefore = await this.stakeManager.delegatorsReward(validatorId) + assertBigNumberEquality(delegationRewardsBefore, this.delegatorReward) + + // complete unstake and remove validator + await checkPoint(w, this.rootChainOwner, this.stakeManager) + + let validatorRewardsAfter = await this.stakeManager.validatorReward(validatorId) + let delegationRewardsAfter = await this.stakeManager.delegatorsReward(validatorId) + + assertBigNumbergt(validatorRewardsAfter, validatorRewardsBefore) + assertBigNumbergt(delegationRewardsAfter, delegationRewardsBefore) + + await stakeManagerUser.withdrawRewardsPOL(validatorId) + const balanceBefore = await this.polToken.balanceOf(user) + + await checkPoint([wallets[2]], this.rootChainOwner, this.stakeManager) + + await stakeManagerUser.withdrawRewardsPOL(validatorId) + assertBigNumberEquality(await this.polToken.balanceOf(user), balanceBefore) + + assertBigNumberEquality(await this.stakeManager.validatorReward(validatorId), new BN(0)) + assertBigNumberEquality(await this.stakeManager.delegatorsReward(validatorId), delegationRewardsAfter) + }) + }) + + describe('reverts', function () { + beforeEach('deploy', async function() { + await freshDeploy.call(this, true) + }) + const user = wallets[2].getChecksumAddressString() + let stakeManagerUser + + beforeEach(doStake(wallets[2])) + + it('when validatorId is invalid', async function () { + stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) + + await expectRevert.unspecified(stakeManagerUser.unstakePOL('999999')) + }) + + it('when user is not staker', async function () { + const validatorId = await this.stakeManager.getValidatorId(user) + const stakeManager3 = this.stakeManager.connect(this.stakeManager.provider.getSigner(3)) + await expectRevert.unspecified(stakeManager3.unstakePOL(validatorId)) + }) + + it('when unstakes 2 times', async function () { + const validatorId = await this.stakeManager.getValidatorId(user) + await stakeManagerUser.unstakePOL(validatorId) + + await expectRevert.unspecified(stakeManagerUser.unstakePOL(validatorId)) + }) + + it('when unstakes during auction', async function () { + const amount = web3.utils.toWei('1200').toString() + const auctionUser = wallets[4].getAddressString() + await this.polToken.mint(auctionUser, amount) + + const polTokenAuctionUser = this.polToken.connect(this.polToken.provider.getSigner(auctionUser)) + const stakeManagerAuctionUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(auctionUser)) + + await polTokenAuctionUser.approve(this.stakeManager.address, amount) + const validatorId = await this.stakeManager.getValidatorId(user) + await stakeManagerAuctionUser.startAuction(validatorId, amount, false, wallets[4].getPublicKeyString()) + await expectRevert.unspecified(stakeManagerUser.unstakePOL(validatorId)) + }) + }) +}) + +describe('unstakeClaim POL', function () { + describe('when user claims right after stake', function () { + before('Fresh Deploy', prepareForTest(1, 10)) + before('Stake', doStake(wallets[2])) + before('Unstake', doUnstake(wallets[2])) + + const user = wallets[2].getAddressString() + + it('must revert', async function () { + const stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) + const ValidatorId = await this.stakeManager.getValidatorId(user) + await expect(stakeManagerUser.unstakeClaimPOL(ValidatorId)) + }) + }) + + describe('when user claims after 1 epoch and 1 dynasty passed', function () { + let dynasties = 1 + const Alice = wallets[2].getChecksumAddressString() + let stakeManagerAlice + + before('Fresh Deploy', prepareForTest(dynasties, 10)) + before('Alice Stake', doStake(wallets[2])) + before('Bob Stake', doStake(wallets[3])) + before('Alice Unstake', doUnstake(wallets[2])) + + before('Checkpoint', async function () { + this.validatorId = await this.stakeManager.getValidatorId(Alice) + this.aliceStakeAmount = await this.stakeManager.validatorStake(this.validatorId) + stakeManagerAlice = this.stakeManager.connect(this.stakeManager.provider.getSigner(Alice)) + + await checkPoint([wallets[3], wallets[2]], this.rootChainOwner, this.stakeManager) + + while (dynasties-- > 0) { + await checkPoint([wallets[3]], this.rootChainOwner, this.stakeManager) + } + }) + + it('must claim', async function () { + this.receipt = await (await stakeManagerAlice.unstakeClaimPOL(this.validatorId)).wait() + }) + + it('must emit Unstaked', async function () { + const stake = await this.stakeManager.currentValidatorSetTotalStake() + await utils.assertInTransaction(this.receipt, StakingInfo, 'Unstaked', { + user: Alice, + validatorId: this.validatorId, + amount: this.aliceStakeAmount, + total: stake + }) + }) + + it('must have correct staked balance', async function () { + const stake = await this.stakeManager.currentValidatorSetTotalStake() + assertBigNumberEquality(stake, walletAmounts[wallets[3].getAddressString()].stakeAmount) + }) + }) + + describe('when user claims next epoch', function () { + before('Fresh Deploy', prepareForTest(1, 10)) + + before('Alice Stake', doStake(wallets[2])) + before('Bob Stake', doStake(wallets[3])) + + before('Alice Unstake', doUnstake(wallets[2])) + + let user + + before('Checkpoint', async function () { + await checkPoint([wallets[3], wallets[2]], this.rootChainOwner, this.stakeManager) + user = wallets[2].getAddressString() + }) + + it('must revert', async function () { + const stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) + const validatorId = await this.stakeManager.getValidatorId(user) + await expect(stakeManagerUser.unstakeClaimPOL(validatorId)) + }) + }) + + describe('when user claims before 1 dynasty passed', function () { + before('Fresh Deploy', prepareForTest(2, 10)) + + before('Alice Stake', doStake(wallets[2])) + before('Bob Stake', doStake(wallets[3])) + + before('Alice Unstake', doUnstake(wallets[2])) + + let user + + before('Checkpoint', async function () { + await checkPoint([wallets[3], wallets[2]], this.rootChainOwner, this.stakeManager) + user = wallets[2].getAddressString() + }) + + it('must revert', async function () { + const stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) + const validatorId = await this.stakeManager.getValidatorId(user) + await expect(stakeManagerUser.unstakeClaimPOL(validatorId)) + }) + }) + + describe('when Alice, Bob and Eve stake, but Alice and Bob claim after 1 epoch and 1 dynasty passed', function () { + before(prepareForTest(1, 10)) + + const Alice = wallets[2] + const Bob = wallets[3] + const Eve = wallets[4] + const stakeAmount = web3.utils.toWei('100') + + before('Alice stake', doStake(Alice, { noMinting: true, stakeAmount })) + before('Bob stake', doStake(Bob, { noMinting: true, stakeAmount })) + before('Eve stake', doStake(Eve, { noMinting: true, stakeAmount })) + + before('Alice unstake', doUnstake(Alice)) + before('Bob unstake', doUnstake(Bob)) + + before('Checkpoint', async function () { + await checkPoint([Eve, Bob, Alice], this.rootChainOwner, this.stakeManager) + await checkPoint([Eve], this.rootChainOwner, this.stakeManager) + }) + + describe('when Alice claims', function () { + const user = Alice.getAddressString() + + it('must claim', async function () { + const stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) + this.validatorId = await this.stakeManager.getValidatorId(user) + this.reward = await this.stakeManager.validatorReward(this.validatorId) + this.receipt = await (await stakeManagerUser.unstakeClaimPOL(this.validatorId)).wait() + }) + + it('must have correct reward', async function () { + assertBigNumberEquality(this.reward, web3.utils.toWei('3000')) + }) + + it('must emit ClaimRewards', async function () { + await utils.assertInTransaction(this.receipt, StakingInfo, 'ClaimRewards', { + validatorId: this.validatorId, + amount: web3.utils.toWei('3000'), + totalAmount: await this.stakeManager.totalRewardsLiquidated() + }) + }) + + it('must have pre-stake + reward - heimdall fee balance', async function () { + let balance = await this.polToken.balanceOf(user) + assertBigNumberEquality( + balance, + new BN(walletAmounts[user].initialBalance).add(new BN(this.reward.toString())).sub(this.defaultHeimdallFee) + ) + }) + }) + + describe('when Bob claims', function () { + const user = Bob.getAddressString() + + it('must claim', async function () { + const stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) + this.validatorId = await this.stakeManager.getValidatorId(user) + this.reward = await this.stakeManager.validatorReward(this.validatorId) + this.receipt = await (await stakeManagerUser.unstakeClaimPOL(this.validatorId)).wait() + }) + + it('must have correct reward', async function () { + assertBigNumberEquality(this.reward, web3.utils.toWei('3000')) + }) + + it('must emit ClaimRewards', async function () { + await utils.assertInTransaction(this.receipt, StakingInfo, 'ClaimRewards', { + validatorId: this.validatorId, + amount: web3.utils.toWei('3000'), + totalAmount: await this.stakeManager.totalRewardsLiquidated() + }) + }) + + it('must have pre-stake + reward - heimdall fee balance', async function () { + let balance = await this.polToken.balanceOf(user) + assertBigNumberEquality( + balance, + new BN(walletAmounts[user].initialBalance).add(new BN(this.reward.toString())).sub(this.defaultHeimdallFee) + ) + }) + }) + + describe('afterwards verification', function () { + it('must have corect number of validators', async function () { + const validatorCount = await this.stakeManager.currentValidatorSetSize() + assertBigNumberEquality(validatorCount, '1') + }) + + it('staked balance must have only Eve balance', async function () { + const stake = await this.stakeManager.currentValidatorSetTotalStake() + assertBigNumberEquality(stake, stakeAmount) + }) + + it('Eve must have correct rewards', async function () { + const validatorId = await this.stakeManager.getValidatorId(Eve.getAddressString()) + this.reward = await this.stakeManager.validatorReward(validatorId) + assertBigNumberEquality(this.reward, web3.utils.toWei('12000')) + }) + }) + }) +}) + +describe('restake POL', function () { + const initialStake = web3.utils.toWei('1000') + const initialStakers = [wallets[0], wallets[1]] + + function doDeploy(acceptDelegation) { + return async function () { + await prepareForTest(8, 8).call(this) + + const checkpointReward = new BN(web3.utils.toWei('10000')) + + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('updateCheckpointReward', [checkpointReward.toString()]) + ) + + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('updateCheckPointBlockInterval', [1]) + ) + + const proposerBonus = 10 + await this.governance.update( + this.stakeManager.address, + this.stakeManager.interface.encodeFunctionData('updateProposerBonus', [proposerBonus]) + ) + + for (const wallet of initialStakers) { + await approveAndStake.call(this, { wallet, stakeAmount: initialStake, acceptDelegation, pol: true }) + } + + // cooldown period + let auctionPeriod = (await this.stakeManager.auctionPeriod()).toNumber() + let currentEpoch = (await this.stakeManager.currentEpoch()).toNumber() + + for (let i = currentEpoch; i <= auctionPeriod; i++) { + await checkPoint(initialStakers, this.rootChainOwner, this.stakeManager) + } + // without 10% proposer bonus + this.validatorReward = checkpointReward + .mul(new BN(100 - proposerBonus)) + .div(new BN(100)) + .mul(new BN(auctionPeriod - currentEpoch)) + this.validatorId = '1' + this.user = initialStakers[0].getAddressString() + this.amount = web3.utils.toWei('100') + + await this.polToken.mint(this.user, this.amount) + this.polTokenUser = this.polToken.connect(this.polToken.provider.getSigner(this.user)) + await this.polTokenUser.approve(this.stakeManager.address, this.amount) + } + } + + function testRestake(withDelegation, withRewards) { + before(doDeploy(withDelegation)) + + before(async function () { + this.oldTotalStaked = await this.stakeManager.totalStaked() + this.validatorOldState = await this.stakeManager.validators(this.validatorId) + + if (!withRewards) { + this.validatorReward = new BN(0) + this.oldReward = await this.stakeManager.validatorReward(this.validatorId) + } + }) + + it('must restake rewards', async function () { + const stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(this.user)) + + this.receipt = await (await stakeManagerUser.restakePOL(this.validatorId, this.amount, withRewards)).wait() + }) + + it('must emit StakeUpdate', async function () { + await utils.assertInTransaction(this.receipt, StakingInfo, 'StakeUpdate', { + validatorId: this.validatorId, + newAmount: this.validatorOldState.amount.add(this.amount.toString()).add(this.validatorReward.toString()) + }) + }) + + it('must emit Restaked', async function () { + await utils.assertInTransaction(this.receipt, StakingInfo, 'Restaked', { + validatorId: this.validatorId, + amount: this.validatorOldState.amount.add(this.amount.toString()).add(this.validatorReward.toString()), + total: this.oldTotalStaked.add(this.amount.toString()).add(this.validatorReward.toString()) + }) + }) + + if (withRewards) { + it('validator rewards must be 0', async function () { + const reward = await this.stakeManager.validatorReward(this.validatorId) + + assertBigNumberEquality(reward, 0) + }) + } else { + it('validator rewards must be untouched', async function () { + const reward = await this.stakeManager.validatorReward(this.validatorId) + + assertBigNumberEquality(reward, this.oldReward) + }) + } + } + + describe('with delegation', function () { + describe('with rewards', function () { + testRestake(true, true) + }) + + describe('without rewards', function () { + testRestake(true, false) + }) + }) + + describe('without delegation', function () { + describe('with rewards', function () { + testRestake(false, true) + }) + + describe('without rewards', function () { + testRestake(false, false) + }) + }) + + describe('reverts', function () { + before(doDeploy(false)) + + before(() => {}) + + it('when validatorId is incorrect', async function () { + const stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(this.user)) + await expect(stakeManagerUser.restakePOL('0', this.amount, false)) + }) + + it('when restake after unstake during same epoch', async function () { + await this.stakeManager.unstakePOL(this.validatorId) + const stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(this.user)) + await expectRevert(stakeManagerUser.restakePOL(this.validatorId, this.amount, false), 'No restaking') + }) + }) +}) diff --git a/test/units/staking/stakeManager/StakeManager.test.js b/test/units/staking/stakeManager/StakeManager.test.js index 5244a648..631eb83d 100644 --- a/test/units/staking/stakeManager/StakeManager.test.js +++ b/test/units/staking/stakeManager/StakeManager.test.js @@ -64,14 +64,16 @@ function testCheckpointing( const ZeroAddr = '0x0000000000000000000000000000000000000000' -describe('StakeManager', async function (accounts) { - accounts = await ethers.getSigners() - accounts = accounts.map((account) => { +describe('StakeManager', function (accounts) { + let owner + before(async ()=>{ + accounts = await ethers.getSigners() + accounts = accounts.map((account) => { return account.address }) - - let owner = accounts[0] - + owner = accounts[0] + }) + describe('initialize', function () { describe('when called directly on implementation', function () { before(freshDeploy) @@ -102,15 +104,6 @@ describe('StakeManager', async function (accounts) { async function batchDeploy() { await Staking.prepareForTest(4, 2).call(this) - this.stakeToken = await TestToken.deploy('MATIC', 'MATIC') - - await this.governance.update( - this.stakeManager.address, - this.stakeManager.interface.encodeFunctionData('setStakingToken', [this.stakeToken.address]) - ) - - await this.stakeToken.mint(this.stakeManager.address, web3.utils.toWei('10000000')) - this.validatorId = '1' this.validatorUser = wallets[0] this.stakeAmount = new BN(web3.utils.toWei('100')) @@ -282,7 +275,9 @@ describe('StakeManager', async function (accounts) { let stakeAmount = web3.utils.toWei('100') function doDeploy(acceptDelegation) { - before('Fresh deploy', freshDeploy) + before('Fresh deploy', async function() { + await freshDeploy.call(this, true) + }) before('Approve and stake', async function () { await approveAndStake.call(this, { wallet: staker, stakeAmount: stakeAmount, acceptDelegation }) @@ -336,7 +331,9 @@ describe('StakeManager', async function (accounts) { describe('checkSignatures', function () { function prepareToTest(stakers, checkpointBlockInterval = 1) { - before('Fresh deploy', freshDeploy) + before('Fresh deploy', async function(){ + await freshDeploy.call(this, true) + }) before('updateCheckPointBlockInterval', async function () { await this.governance.update( this.stakeManager.address, @@ -821,7 +818,9 @@ describe('StakeManager', async function (accounts) { }) describe('when payload is invalid', function () { - beforeEach(freshDeploy) + beforeEach('Fresh deploy', async function(){ + await freshDeploy.call(this, true) + }) beforeEach('Prepare to test', async function () { this.amount = new BN(web3.utils.toWei('200')) this.wallets = [wallets[2]] @@ -921,7 +920,7 @@ describe('StakeManager', async function (accounts) { } async function doDeploy() { - await freshDeploy.call(this) + await freshDeploy.call(this, true) this.checkpointIndex = 0 this.validators = [] @@ -942,7 +941,7 @@ describe('StakeManager', async function (accounts) { this.index = 0 } - describe('Deploying and staking with 4 validators...', async function () { + describe('Deploying and staking with 4 validators...', function () { const AliceValidatorId = 1 const firstFeeToClaim = new BN(web3.utils.toWei('25')) @@ -985,7 +984,9 @@ describe('StakeManager', async function (accounts) { }) describe("when trying to checkpoint when 1 of the validators unstakes with more than 1/3 stake and don't sign", function () { - before('Fresh Deploy', freshDeploy) + before('Fresh deploy', async function(){ + await freshDeploy.call(this, true) + }) before('Alice And Bob Stake', async function () { for (const wallet of [wallets[2], wallets[3]]) { await approveAndStake.call(this, { @@ -1008,8 +1009,9 @@ describe('StakeManager', async function (accounts) { describe('dethroneAndStake', function () { describe('when from is not stake manager', function () { - before('Fresh Deploy', freshDeploy) - + before('Fresh deploy', async function(){ + await freshDeploy.call(this, true) + }) it('reverts', async function () { await expectRevert( this.stakeManager.dethroneAndStake( @@ -1028,7 +1030,9 @@ describe('StakeManager', async function (accounts) { describe('setDelegationEnabled', function () { describe('when from is governance', function () { - before(freshDeploy) + before('Fresh deploy', async function(){ + await freshDeploy.call(this, true) + }) it('must disable delegation', async function () { await this.governance.update( @@ -1054,8 +1058,9 @@ describe('StakeManager', async function (accounts) { }) describe('when from is not governance', function () { - before(freshDeploy) - + before('Fresh deploy', async function(){ + await freshDeploy.call(this, true) + }) it('reverts', async function () { await expectRevert(this.stakeManager.setDelegationEnabled(false), 'Only governance contract is authorized') }) @@ -1068,7 +1073,7 @@ describe('StakeManager', async function (accounts) { const userOriginalPubKey = wallets[3].getPublicKeyString() async function doDeploy() { - await freshDeploy.call(this) + await freshDeploy.call(this, true) const amount = web3.utils.toWei('200') for (const wallet of w) { @@ -1271,7 +1276,9 @@ describe('StakeManager', async function (accounts) { describe('updateDynastyValue', function () { describe('when set dynasty to 10', function () { - before(freshDeploy) + before('Fresh deploy', async function(){ + await freshDeploy.call(this, true) + }) it('must update dynasty', async function () { this.receipt = await ( @@ -1306,7 +1313,9 @@ describe('StakeManager', async function (accounts) { }) describe('when set dynasty to 0', function () { - before(freshDeploy) + before('Fresh deploy', async function(){ + await freshDeploy.call(this, true) + }) it('must revert', async function () { await expectRevert.unspecified(this.stakeManager.updateDynastyValue('0')) @@ -1314,7 +1323,9 @@ describe('StakeManager', async function (accounts) { }) describe('when from is not governance', function () { - before(freshDeploy) + before('Fresh deploy', async function(){ + await freshDeploy.call(this, true) + }) it('reverts', async function () { const stakeManagerOwner = this.stakeManager.connect(this.stakeManager.provider.getSigner(owner)) @@ -1325,7 +1336,9 @@ describe('StakeManager', async function (accounts) { describe('updateCheckpointReward', function () { describe('when set reward to 20', function () { - before(freshDeploy) + before('Fresh deploy', async function(){ + await freshDeploy.call(this, true) + }) it('must update', async function () { this.oldReward = await this.stakeManager.CHECKPOINT_REWARD() @@ -1346,7 +1359,9 @@ describe('StakeManager', async function (accounts) { }) describe('when set reward to 0', function () { - before(freshDeploy) + before('Fresh deploy', async function(){ + await freshDeploy.call(this, true) + }) it('must revert', async function () { await expectRevert.unspecified(this.stakeManager.updateCheckpointReward(0)) @@ -1354,7 +1369,9 @@ describe('StakeManager', async function (accounts) { }) describe('when from is not governance', function () { - before(freshDeploy) + before('Fresh deploy', async function(){ + await freshDeploy.call(this, true) + }) it('reverts', async function () { const stakeManagerOwner = this.stakeManager.connect(this.stakeManager.provider.getSigner(owner)) @@ -1370,7 +1387,7 @@ describe('StakeManager', async function (accounts) { let _wallets = [Alice, Bob] async function doDeploy() { - await freshDeploy.call(this) + await freshDeploy.call(this, true) this.amount = new BN(web3.utils.toWei('200')) this.totalStaked = new BN(0) @@ -1475,14 +1492,14 @@ describe('StakeManager', async function (accounts) { const fee = new BN(web3.utils.toWei('50')).toString() async function doDeploy() { - await freshDeploy.call(this) + await freshDeploy.call(this, true) await approveAndStake.call(this, { wallet, stakeAmount: amount }) } function testTopUp(user) { it('must top up', async function () { - const stakeTokenUser = this.stakeToken.connect(this.stakeToken.provider.getSigner(user)) - await stakeTokenUser.approve(this.stakeManager.address, fee) + const stakeTokenUser = this.polToken.connect(this.polToken.provider.getSigner(user)) + await (await stakeTokenUser.approve(this.stakeManager.address, fee)).wait() const stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(user)) this.receipt = await (await stakeManagerUser.topUpForFee(user, fee)).wait() @@ -1542,8 +1559,8 @@ describe('StakeManager', async function (accounts) { runTopUpTests(async function () { await doDeploy.call(this) const mintAmount = web3.utils.toWei('10000') - await this.stakeToken.mint(user, mintAmount) - const stakeTokenUser = this.stakeToken.connect(this.stakeToken.provider.getSigner(user)) + await this.polToken.mint(user, mintAmount) + const stakeTokenUser = this.polToken.connect(this.polToken.provider.getSigner(user)) await stakeTokenUser.approve(this.stakeManager.address, mintAmount.toString()) }, user) }) @@ -1564,7 +1581,7 @@ describe('StakeManager', async function (accounts) { beforeEach(doDeploy) it('when user approves less than fee', async function () { - const stakeTokenValidatorUser = this.stakeToken.connect(this.stakeToken.provider.getSigner(validatorUser)) + const stakeTokenValidatorUser = this.polToken.connect(this.polToken.provider.getSigner(validatorUser)) await stakeTokenValidatorUser.approve(this.stakeManager.address, new BN(fee).sub(new BN(1)).toString()) const stakeManagerValidatorUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(validatorUser)) @@ -1628,7 +1645,7 @@ describe('StakeManager', async function (accounts) { } async function doDeploy() { - await freshDeploy.call(this) + await freshDeploy.call(this, true) this.checkpointIndex = 0 this.validators = [] @@ -1666,7 +1683,7 @@ describe('StakeManager', async function (accounts) { ) } - const stakeTokenValidatorUser = this.stakeToken.connect(this.stakeToken.provider.getSigner(validatorAddr)) + const stakeTokenValidatorUser = this.polToken.connect(this.polToken.provider.getSigner(validatorAddr)) await stakeTokenValidatorUser.approve(this.stakeManager.address, fee.toString()) const stakeManagerValidatorUser = this.stakeManager.connect( @@ -1699,7 +1716,7 @@ describe('StakeManager', async function (accounts) { }) it('balance must increase by fee', async function () { - const newBalance = await this.stakeToken.balanceOf(this.user) + const newBalance = await this.polToken.balanceOf(this.user) assertBigNumberEquality(this.beforeClaimBalance.add(this.claimedFee.toString()), newBalance) }) @@ -1709,7 +1726,7 @@ describe('StakeManager', async function (accounts) { }) } - describe('when Alice topups once and claims 2 times', async function () { + describe('when Alice topups once and claims 2 times', function () { const AliceValidatorId = 1 const totalFee = new BN(web3.utils.toWei('100')).toString() const firstFeeToClaim = new BN(web3.utils.toWei('25')) @@ -1724,7 +1741,7 @@ describe('StakeManager', async function (accounts) { before('top up', async function () { this.user = this.validatorsWallets[AliceValidatorId].getChecksumAddressString() - const stakeTokenUser = this.stakeToken.connect(this.stakeToken.provider.getSigner(this.user)) + const stakeTokenUser = this.polToken.connect(this.polToken.provider.getSigner(this.user)) await stakeTokenUser.approve(this.stakeManager.address, totalFee) const stakeManagerUser = this.stakeManager.connect(this.stakeManager.provider.getSigner(this.user)) @@ -1738,7 +1755,7 @@ describe('StakeManager', async function (accounts) { this.leaf = createLeafFrom.call(this, this.user, 0) this.fee = firstFeeToClaim this.claimedFee = this.fee - this.beforeClaimBalance = await this.stakeToken.balanceOf(this.user) + this.beforeClaimBalance = await this.polToken.balanceOf(this.user) this.beforeClaimTotalHeimdallFee = await this.stakeManager.totalHeimdallFee() }) @@ -1752,7 +1769,7 @@ describe('StakeManager', async function (accounts) { this.leaf = createLeafFrom.call(this, this.user, 0) this.fee = secondFeeToClaim this.claimedFee = secondFeeToClaim.sub(firstFeeToClaim) - this.beforeClaimBalance = await this.stakeToken.balanceOf(this.user) + this.beforeClaimBalance = await this.polToken.balanceOf(this.user) this.beforeClaimTotalHeimdallFee = await this.stakeManager.totalHeimdallFee() }) @@ -1784,7 +1801,7 @@ describe('StakeManager', async function (accounts) { before(async function () { this.tree = await feeCheckpoint.call(this, AliceValidatorId, 0, 22) this.leaf = createLeafFrom.call(this, this.user, 0) - this.beforeClaimBalance = await this.stakeToken.balanceOf(this.user) + this.beforeClaimBalance = await this.polToken.balanceOf(this.user) }) it('Bob must fail withdrawing', async function () { @@ -1835,7 +1852,7 @@ describe('StakeManager', async function (accounts) { describe('claims 1st time', function () { before(async function () { - this.beforeClaimBalance = await this.stakeToken.balanceOf(this.user) + this.beforeClaimBalance = await this.polToken.balanceOf(this.user) this.tree = this.trees[0] this.fee = firstFee this.leaf = createLeafFrom.call(this, AliceValidatorId, 0) @@ -1850,7 +1867,7 @@ describe('StakeManager', async function (accounts) { this.fee = secondFee this.index = 0 this.leaf = createLeafFrom.call(this, this.user, 1) - this.beforeClaimBalance = await this.stakeToken.balanceOf(this.user) + this.beforeClaimBalance = await this.polToken.balanceOf(this.user) }) testAliceClaim() @@ -1880,7 +1897,7 @@ describe('StakeManager', async function (accounts) { beforeEach(async function () { this.tree = await feeCheckpoint.call(this, this.validatorId, 0, 22) this.leaf = createLeafFrom.call(this, this.user, 0) - this.beforeClaimBalance = await this.stakeToken.balanceOf(this.user) + this.beforeClaimBalance = await this.polToken.balanceOf(this.user) this.proof = utils.bufferToHex(Buffer.concat(this.tree.getProof(this.leaf))) }) @@ -1924,16 +1941,16 @@ describe('StakeManager', async function (accounts) { before('deploy', doDeploy) before(async function () { - await this.stakeToken.mint(Alice.getAddressString(), aliceBidAmount) - const stakeTokenAlive = this.stakeToken.connect(this.stakeToken.provider.getSigner(Alice.getAddressString())) - await stakeTokenAlive.approve(this.stakeManager.address, aliceBidAmount) + await this.polToken.mint(Alice.getAddressString(), aliceBidAmount) + const polTokenAlice = this.polToken.connect(this.polToken.provider.getSigner(Alice.getAddressString())) + await polTokenAlice.approve(this.stakeManager.address, aliceBidAmount) - await this.stakeToken.mint(Bob.getAddressString(), bobBidAmount) - const stakeTokenBob = this.stakeToken.connect(this.stakeToken.provider.getSigner(Bob.getAddressString())) - await stakeTokenBob.approve(this.stakeManager.address, bobBidAmount) + await this.polToken.mint(Bob.getAddressString(), bobBidAmount) + const polTokenBob = this.polToken.connect(this.polToken.provider.getSigner(Bob.getAddressString())) + await polTokenBob.approve(this.stakeManager.address, bobBidAmount) - this.userOldBalance = await this.stakeToken.balanceOf(Alice.getAddressString()) - this.bobOldBalance = await this.stakeToken.balanceOf(Bob.getAddressString()) + this.userOldBalance = await this.polToken.balanceOf(Alice.getAddressString()) + this.bobOldBalance = await this.polToken.balanceOf(Bob.getAddressString()) this.validatorId = '1' this.initialStakeAmount = initialStakeAmount @@ -1947,7 +1964,7 @@ describe('StakeManager', async function (accounts) { testStartAuction(Bob.getChecksumAddressString(), Bob.getPublicKeyString(), bobBidAmount) it('Alice must get her bid back', async function () { - const currentBalance = await this.stakeToken.balanceOf(Alice.getAddressString()) + const currentBalance = await this.polToken.balanceOf(Alice.getAddressString()) assertBigNumberEquality(this.userOldBalance, currentBalance) }) }) @@ -1972,8 +1989,8 @@ describe('StakeManager', async function (accounts) { const stakeManager3 = this.stakeManager.connect(this.stakeManager.provider.getSigner(3)) await stakeManager3.startAuction(this.validatorId, this.amount, false, wallets[3].getPublicKeyString()) - const stakeToken3 = this.stakeToken.connect(this.stakeToken.provider.getSigner(3)) - await stakeToken3.approve(this.stakeManager.address, web3.utils.toWei('1')) + const polToken3 = this.polToken.connect(this.polToken.provider.getSigner(3)) + await polToken3.approve(this.stakeManager.address, web3.utils.toWei('1')) await expectRevert( stakeManager3.confirmAuctionBid(this.validatorId, web3.utils.toWei('1')), 'Not allowed before auctionPeriod' @@ -2060,12 +2077,12 @@ describe('StakeManager', async function (accounts) { await checkPoint(_initialStakers, this.rootChainOwner, this.stakeManager) } this.amount = web3.utils.toWei('500') - const stakeToken3 = this.stakeToken.connect(this.stakeToken.provider.getSigner(3)) - await stakeToken3.approve(this.stakeManager.address, this.amount) + const polToken3 = this.polToken.connect(this.polToken.provider.getSigner(3)) + await polToken3.approve(this.stakeManager.address, this.amount) } }) - describe('confirmAuctionBid', function () { + describe.skip('confirmAuctionBid', function () { const initialStakers = [wallets[1], wallets[2]] const bidAmount = new BN(web3.utils.toWei('1200')) const initialStakeAmount = web3.utils.toWei('200') @@ -2082,17 +2099,17 @@ describe('StakeManager', async function (accounts) { await approveAndStake.call(this, { wallet, stakeAmount: initialStakeAmount }) } - const stakeToken3 = this.stakeToken.connect(this.stakeToken.provider.getSigner(3)) + const polToken3 = this.polToken.connect(this.polToken.provider.getSigner(3)) this.amount = web3.utils.toWei('500') - await stakeToken3.approve(this.stakeManager.address, this.amount) + await polToken3.approve(this.stakeManager.address, this.amount) // bid const mintAmount = bidAmount.add(new BN(this.heimdallFee || this.defaultHeimdallFee)).toString() - await this.stakeToken.mint(this.bidder, mintAmount) - const stakeTokenBidder = this.stakeToken.connect(this.stakeToken.provider.getSigner(this.bidder)) - await stakeTokenBidder.approve(this.stakeManager.address, mintAmount) + await this.polToken.mint(this.bidder, mintAmount) + const polTokenBidder = this.polToken.connect(this.polToken.provider.getSigner(this.bidder)) + await polTokenBidder.approve(this.stakeManager.address, mintAmount) - this.bidderBalanceBeforeAuction = await this.stakeToken.balanceOf(this.bidder) + this.bidderBalanceBeforeAuction = await this.polToken.balanceOf(this.bidder) this.totalStakedBeforeAuction = await this.stakeManager.totalStaked() const stakeManagerBidder = this.stakeManager.connect(this.stakeManager.provider.getSigner(this.bidder)) @@ -2128,7 +2145,7 @@ describe('StakeManager', async function (accounts) { before(async function () { this.prevValidatorAddr = initialStakers[0].getChecksumAddressString() - this.prevValidatorOldBalance = await this.stakeToken.balanceOf(this.prevValidatorAddr) + this.prevValidatorOldBalance = await this.polToken.balanceOf(this.prevValidatorAddr) this.validator = await this.stakeManager.validators(this.validatorId) this.reward = await this.stakeManager.validatorReward(this.validatorId) @@ -2152,10 +2169,10 @@ describe('StakeManager', async function (accounts) { prepareToTest() before(async function () { let restakeAmount = web3.utils.toWei('10000') - await this.stakeToken.mint(this.prevValidatorAddr, restakeAmount) + await this.polToken.mint(this.prevValidatorAddr, restakeAmount) - const stakeTokenValidator = this.stakeToken.connect( - this.stakeToken.provider.getSigner(this.prevValidatorAddr) + const stakeTokenValidator = this.polToken.connect( + this.polToken.provider.getSigner(this.prevValidatorAddr) ) await stakeTokenValidator.approve(this.stakeManager.address, restakeAmount) @@ -2224,7 +2241,7 @@ describe('StakeManager', async function (accounts) { }) }) - describe('auction with delegator, 3 validators initially', async function () { + describe('auction with delegator, 3 validators initially', function () { const initialStakers = [wallets[1], wallets[2]] const delegatedValidatorId = '3' const delegator = wallets[3].getChecksumAddressString() @@ -2271,12 +2288,12 @@ describe('StakeManager', async function (accounts) { this.heimdallFee = this.defaultHeimdallFee const approveAmount = new BN(bidAmount).add(this.heimdallFee).toString() - await this.stakeToken.mint(auctionValidatorAddr, approveAmount) - const stakeTokenValidator = this.stakeToken.connect(this.stakeToken.provider.getSigner(auctionValidatorAddr)) - await stakeTokenValidator.approve(this.stakeManager.address, approveAmount) + await this.polToken.mint(auctionValidatorAddr, approveAmount) + const polTokenValidator = this.polToken.connect(this.polToken.provider.getSigner(auctionValidatorAddr)) + await polTokenValidator.approve(this.stakeManager.address, approveAmount) this.totalStakedBeforeAuction = await this.stakeManager.totalStaked() - this.bidderBalanceBeforeAuction = await this.stakeToken.balanceOf(auctionValidatorAddr) + this.bidderBalanceBeforeAuction = await this.polToken.balanceOf(auctionValidatorAddr) }) describe('when new validator bids', function () { @@ -2366,9 +2383,9 @@ describe('StakeManager', async function (accounts) { }) it('must bid', async function () { - await this.stakeToken.mint(bidder, bidAmount) - const stakeToken3 = this.stakeToken.connect(this.stakeToken.provider.getSigner(3)) - await stakeToken3.approve(this.stakeManager.address, bidAmount) + await this.polToken.mint(bidder, bidAmount) + const polToken3 = this.polToken.connect(this.polToken.provider.getSigner(3)) + await polToken3.approve(this.stakeManager.address, bidAmount) const stakeManager3 = this.stakeManager.connect(this.stakeManager.provider.getSigner(3)) await stakeManager3.startAuction(this.validatorId, bidAmount, false, bidderPubKey) }) @@ -2396,7 +2413,7 @@ describe('StakeManager', async function (accounts) { let stakeToken9, stakeManager9 async function prepareForTest() { - await freshDeploy.call(this) + await freshDeploy.call(this, true) stakeManager9 = this.stakeManager.connect(this.stakeManager.provider.getSigner(9)) stakeToken9 = this.stakeToken.connect(this.stakeToken.provider.getSigner(9)) await this.governance.update( @@ -2532,7 +2549,7 @@ describe('StakeManager', async function (accounts) { }) }) - describe('when Chad delegates to Alice then migrates partialy to Bob', async function () { + describe('when Chad delegates to Alice then migrates partialy to Bob', function () { const aliceId = '2' const bobId = '8' const delegator = wallets[9].getChecksumAddressString() @@ -2547,7 +2564,7 @@ describe('StakeManager', async function (accounts) { aliceContract = await ValidatorShare.attach(aliceValidator.contractAddress) }) - describe('Chad delegates to Alice', async function () { + describe('Chad delegates to Alice', function () { it('Should delegate', async function () { this.receipt = await (await buyVoucher(aliceContract, delegationAmount, delegator)).wait() }) @@ -2582,7 +2599,7 @@ describe('StakeManager', async function (accounts) { }) }) - describe('Chad migrates delegation to Bob', async function () { + describe('Chad migrates delegation to Bob', function () { it('Should migrate', async function () { this.receipt = await (await stakeManager9.migrateDelegation(aliceId, bobId, migrationAmount)).wait() }) @@ -2690,7 +2707,7 @@ describe('StakeManager', async function (accounts) { }) }) - describe('when Chad migrates with more tokens than his delegation amount', async function () { + describe('when Chad migrates with more tokens than his delegation amount', function () { const aliceId = '2' const bobId = '8' const delegator = wallets[9].getChecksumAddressString() @@ -2758,7 +2775,7 @@ describe('StakeManager', async function (accounts) { }) it('balance must decrease by bid amount', async function () { - assertBigNumberEquality(await this.stakeToken.balanceOf(address), this.userOldBalance.sub(bidAmount).toString()) + assertBigNumberEquality(await this.polToken.balanceOf(address), this.userOldBalance.sub(bidAmount).toString()) }) } @@ -2806,7 +2823,7 @@ describe('StakeManager', async function (accounts) { }) it('previous validator must get his reward', async function () { - let prevValidatorBalance = await this.stakeToken.balanceOf(this.prevValidatorAddr) + let prevValidatorBalance = await this.polToken.balanceOf(this.prevValidatorAddr) assertBigNumberEquality(prevValidatorBalance, this.prevValidatorOldBalance.add(this.reward)) }) @@ -2819,7 +2836,7 @@ describe('StakeManager', async function (accounts) { }) it('bidder balance must be correct', async function () { - const currentBalance = await this.stakeToken.balanceOf(this.bidder) + const currentBalance = await this.polToken.balanceOf(this.bidder) assertBigNumberEquality( this.bidderBalanceBeforeAuction.sub(this.bidAmount.toString()).sub(this.heimdallFee.toString()), currentBalance