diff --git a/.env.sample b/.env.sample index 9c9a754..e16d27a 100644 --- a/.env.sample +++ b/.env.sample @@ -4,6 +4,7 @@ DEPLOYER_PRIVATE_KEY= # Testing purposes DEPLOY_CONFIG=./artifacts/holesky/deploy-holesky.json # Specify the chain to deploy or test on -CHAIN=mainnet | holesky +CHAIN=mainnet | holesky # Where to store the deployment artifacts. Default is ./artifacts/local ARTIFACTS_DIR=./artifacts/local +ETHERSCAN_API_KEY=key diff --git a/.gitignore b/.gitignore index 1cb5839..12aa7a2 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ lcov.info !.yarn/versions lib/* + +scc-report.* +scm-report.* diff --git a/.solhint.json b/.solhint.json index ff5e365..8e53812 100644 --- a/.solhint.json +++ b/.solhint.json @@ -1,16 +1,18 @@ { "extends": "solhint:recommended", - "plugins": [""], + "plugins": ["lido-csm"], "rules": { + "constructor-syntax": "error", "compiler-version": ["error", "0.8.28"], "no-inline-assembly": "off", "no-unused-import": "error", - "func-named-parameters": "error", + "func-named-parameters": ["warn", 5], "func-visibility": ["error", { "ignoreConstructors": true }], "reason-string": ["warn", { "maxLength": 64 }], "immutable-vars-naming": ["error", { "immutablesAsConstants": true }], - "var-name-mixedcase": "error", - "func-name-mixedcase": "error", + "var-name-mixedcase": "warn", + "func-name-mixedcase": "warn", + "foundry-test-functions": ["off", ["setUp"]], "no-global-import": "error", "ordering": "warn", "gas-calldata-parameters": "error", diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index 09b5e8d..d2fd580 100644 Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..62a2a5c --- /dev/null +++ b/Justfile @@ -0,0 +1,186 @@ +set dotenv-load + +chain := env_var_or_default("CHAIN", "mainnet") +deploy_script_name := if chain == "mainnet" { + "DeployMainnet" +} else if chain == "holesky" { + "DeployHolesky" +} else { + error("Unsupported chain " + chain) +} + +upgrade_script_name := if chain == "mainnet" { + "UpgradeMainnet" +} else if chain == "holesky" { + "UpgradeHolesky" +} else { + error("Unsupported chain " + chain) +} + +deploy_script_path := "script" / deploy_script_name + ".s.sol:" + deploy_script_name +upgrade_script_path := "script" / upgrade_script_name + ".s.sol:" + upgrade_script_name + +anvil_host := env_var_or_default("ANVIL_IP_ADDR", "127.0.0.1") +anvil_port := "8545" +anvil_rpc_url := "http://" + anvil_host + ":" + anvil_port + +default: clean deps build test-all + +build *args: + forge build --force {{args}} + +clean: + forge clean + rm -rf cache broadcast out node_modules + +deps: + yarn workspaces focus --all --production + +deps-dev: + yarn workspaces focus --all && npx husky install + +lint-solhint: + yarn lint:solhint + +lint-fix: + yarn lint:fix + +lint: + yarn lint:check + +test-all: + just test-unit & + just test-local + +test-unit *args: + forge test --no-match-path 'test/fork/*' -vvv {{args}} + +test-deployment *args: + forge test --match-path 'test/fork/*' -vvv {{args}} + +gas-report: + #!/usr/bin/env python + + import subprocess + import re + + command = "just test-unit --nmt 'testFuzz.+' --gas-report" + output = subprocess.check_output(command, shell=True, text=True) + + lines = output.split('\n') + + filename = 'GAS.md' + to_print = False + skip_next = False + + with open(filename, 'w') as fh: + for line in lines: + if skip_next: + skip_next = False + continue + + if line.startswith('|'): + to_print = True + + if line.startswith('| Deployment Cost'): + to_print = False + skip_next = True + + if re.match(r"Ran \d+ test suites", line): + break + + if to_print: + fh.write(line + '\n') + + print(f"Done. Gas report saved to {filename}") + +coverage *args: + FOUNDRY_PROFILE=coverage forge coverage --no-match-path 'test/fork/*' {{args}} + +# Run coverage and save the report in LCOV file. +coverage-lcov *args: + FOUNDRY_PROFILE=coverage forge coverage --no-match-path 'test/fork/*' --report lcov {{args}} + +diffyscan-contracts *args: + yarn generate:diffyscan {{args}} + +make-fork *args: + @if pgrep -x "anvil" > /dev/null; \ + then just _warn "anvil process is already running in the background. Make sure it's connected to the right network and in the right state."; \ + else anvil -f ${RPC_URL} --host {{anvil_host}} --port {{anvil_port}} --config-out localhost.json {{args}}; \ + fi + +kill-fork: + @-pkill anvil && just _warn "anvil process is killed" + +# deploy production +deploy *args: + forge script {{deploy_script_path}} --rpc-url {{anvil_rpc_url}} --broadcast --slow {{args}} + +deploy-prod *args: + just _warn "The current `tput bold`chain={{chain}}`tput sgr0` with the following rpc url: $RPC_URL" + ARTIFACTS_DIR=./artifacts/latest/ just _deploy-prod-confirm {{args}} + + cp ./broadcast/{{deploy_script_name}}.s.sol/`cast chain-id --rpc-url=$RPC_URL`/run-latest.json \ + ./artifacts/latest/transactions.json + +[confirm("You are about to broadcast deployment transactions to the network. Are you sure?")] +_deploy-prod-confirm *args: + just _deploy-prod --broadcast --verify {{args}} + +deploy-prod-dry *args: + just _deploy-prod {{args}} + +verify-prod *args: + just _warn "Pass --chain=your_chain manually. e.g. --chain=holesky for testnet deployment" + forge script {{deploy_script_path}} --rpc-url ${RPC_URL} --verify {{args}} --unlocked + +_deploy-prod *args: + forge script {{deploy_script_path}} --force --rpc-url ${RPC_URL} {{args}} + +# upgrade production +upgrade *args: + forge script {{upgrade_script_path}} --rpc-url {{anvil_rpc_url}} --broadcast --slow {{args}} + +upgrade-prod *args: + just _warn "The current `tput bold`chain={{chain}}`tput sgr0` with the following rpc url: $RPC_URL" + ARTIFACTS_DIR=./artifacts/latest/ just _upgrade-prod-confirm {{args}} + + cp ./broadcast/{{upgrade_script_name}}.s.sol/`cast chain-id --rpc-url=$RPC_URL`/run-latest.json \ + ./artifacts/latest/transactions.json + +[confirm("You are about to broadcast upgrade transactions to the network. Are you sure?")] +_upgrade-prod-confirm *args: + just _upgrade-prod --broadcast --verify {{args}} + +upgrade-prod-dry *args: + just _upgrade-prod {{args}} + +_upgrade-prod *args: + forge script {{upgrade_script_path}} --force --rpc-url ${RPC_URL} {{args}} + +# local deployment +deploy-local: + just make-fork & + @while ! echo exit | nc {{anvil_host}} {{anvil_port}} > /dev/null; do sleep 1; done + ARTIFACTS_DIR=./artifacts/local/ just deploy + just _warn "anvil is kept running in the background: {{anvil_rpc_url}}" + +upgrade-local: + just make-fork & + @while ! echo exit | nc {{anvil_host}} {{anvil_port}} > /dev/null; do sleep 1; done + ARTIFACTS_DIR=./artifacts/local/ just upgrade + just _warn "anvil is kept running in the background: {{anvil_rpc_url}}" + +test-local *args: + just make-fork --silent & + @while ! echo exit | nc {{anvil_host}} {{anvil_port}} > /dev/null; do sleep 1; done + DEPLOYER_PRIVATE_KEY=`cat localhost.json | jq -r ".private_keys[0]"` \ + ARTIFACTS_DIR=./artifacts/local/ just deploy --silent + DEPLOY_CONFIG=./artifacts/local/deploy-{{chain}}.json \ + RPC_URL={{anvil_rpc_url}} \ + just test-deployment {{args}} + just kill-fork + +_warn message: + @tput setaf 3 && printf "[WARNING]" && tput sgr0 && echo " {{message}}" diff --git a/artifacts/holesky/.gikeep b/artifacts/holesky/.gikeep new file mode 100644 index 0000000..e69de29 diff --git a/artifacts/holesky/deploy-holesky.json b/artifacts/holesky/deploy-holesky.json new file mode 100644 index 0000000..12c34da --- /dev/null +++ b/artifacts/holesky/deploy-holesky.json @@ -0,0 +1,7 @@ +{ + "CCR": "0x5e30e8958a4361ac7a7C4FcE978FF18f70E915a2", + "CCRImpl": "0x76773E5A432113238be489F9A7Bf716e8dC4a015", + "ChainId": 17000, + "DeployParams": "0x00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000f4240", + "LidoLocator": "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" +} diff --git a/artifacts/holesky/transactions.json b/artifacts/holesky/transactions.json new file mode 100644 index 0000000..dcfa80e --- /dev/null +++ b/artifacts/holesky/transactions.json @@ -0,0 +1,648 @@ +{ + "transactions": [ + { + "hash": "0xca7a99583f33e0480dd1506d4616b0fa8770cf7d8a37cd9f258ad7f9aaa66582", + "transactionType": "CREATE", + "contractName": "CredibleCommitmentCurationProvider", + "contractAddress": "0x73cca4dd9e58fa3abca22d55ee81ae613d958980", + "function": null, + "arguments": [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "gas": "0x37fa87", + "value": "0x0", + "input": "0x60c060405234801561000f575f5ffd5b5060405161330338038061330383398101604081905261002e91610128565b6001600160a01b03821661005557604051630f05a38b60e41b815260040160405180910390fd5b6001600160a01b03821660805260a081905261006f610076565b505061015f565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156100c65760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146101255780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f5f60408385031215610139575f5ffd5b82516001600160a01b038116811461014f575f5ffd5b6020939093015192949293505050565b60805160a05161317c6101875f395f611cba01525f81816104ab0152612370015261317c5ff3fe608060405234801561000f575f5ffd5b50600436106101d1575f3560e01c80638aa10435116100fe578063b10f63d81161009e578063d4eec5a61161006e578063d4eec5a614610478578063d547741f14610480578063d6dc6ae714610493578063dbba4b48146104a6575f5ffd5b8063b10f63d814610404578063ba6bc8d514610417578063c3f909d41461042a578063ca15c87314610465575f5ffd5b8063a217fddf116100d9578063a217fddf146103b6578063a3246ad3146103bd578063a3714e07146103dd578063ad32563d146103f0575f5ffd5b80638aa10435146103585780639010d07c1461037857806391d14854146103a3575f5ffd5b8063389ed267116101745780635c975abb116101445780635c975abb1461031357806370bc09f61461032a5780638456cb591461033d578063893d004e14610345575f5ffd5b8063389ed267146102ae5780633c074eb3146102d55780633f4ba83a146102e85780635865c60c146102f0575f5ffd5b80632de03aa1116101af5780632de03aa11461024e5780632de0920c146102755780632f2ff15d1461028857806336568abe1461029b575f5ffd5b806301ffc9a7146101d557806313e09453146101fd578063248a9ca314610212575b5f5ffd5b6101e86101e33660046125ce565b6104cd565b60405190151581526020015b60405180910390f35b61021061020b36600461261d565b6104f7565b005b61024061022036600461268a565b5f9081525f5160206131305f395f51905f52602052604090206001015490565b6040519081526020016101f4565b6102407f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b6102106102833660046126b1565b6106ba565b610210610296366004612779565b610942565b6102106102a9366004612779565b610978565b6102407f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b6102106102e33660046127a7565b6109b0565b610210610a3b565b6103036102fe3660046127d3565b610a70565b6040516101f49493929190612826565b5f5160206131505f395f51905f525460ff166101e8565b6102106103383660046128e3565b610aa0565b610210610aca565b61021061035336600461293c565b610afc565b610360610be1565b6040516001600160401b0390911681526020016101f4565b61038b610386366004612984565b610c18565b6040516001600160a01b0390911681526020016101f4565b6101e86103b1366004612779565b610c45565b6102405f81565b6103d06103cb36600461268a565b610c7b565b6040516101f491906129a4565b6103036103eb3660046127a7565b610cab565b6102405f5160206130f05f395f51905f5281565b6102106104123660046129fc565b610cdd565b610210610425366004612a39565b610de3565b610432610eea565b604080516001600160401b03958616815293851660208501529184169183019190915290911660608201526080016101f4565b61024061047336600461268a565b610f18565b610210610f3c565b61021061048e366004612779565b61100d565b6102106104a13660046127a7565b61103d565b61038b7f000000000000000000000000000000000000000000000000000000000000000081565b5f6001600160e01b03198216635a05180f60e01b14806104f157506104f182611107565b92915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f8115801561053b5750825b90505f826001600160401b031660011480156105565750303b155b905081158015610564575080155b156105825760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156105ac57845460ff60401b1916600160401b1785555b6001600160a01b038a166105d3576040516319467aa760e11b815260040160405180910390fd5b6105db61113b565b6105e361114d565b6105ed5f8b611155565b506106055f5160206130f05f395f51905f528b611155565b506106307f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d8b611155565b5061065b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c78b611155565b5061066889898989611197565b83156106ae57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b6106c26112ce565b6106ca612514565b6001600160a01b0386166106f157604051634434240960e11b815260040160405180910390fd5b6106fc8189896112fe565b80606001516001600160a01b0316336001600160a01b031614610732576040516349ad9b0960e11b815260040160405180910390fd5b8060a0015161075457604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b038716604089901b62ffffff60401b16175f61077682611312565b90505f61078282611378565b8051909150156107a5576040516342ee68b560e01b815260040160405180910390fd5b80604001516107c7576040516328a5715760e21b815260040160405180910390fd5b6107d1838a611476565b6040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc9060200160405180910390a3604080516060810182526001600160401b03431681525f602082018190529181019190915261084f90849061152a565b61085b84848a8a61158b565b604080516020601f88018190048102820183018352810187815261089e928291908a908a90819085018382808284375f920191909152505050915250849061162c565b896001600160401b03168b62ffffff167f8ae0affaa3c6c49aee34fd262b42a1ed56bf92a700857bddf013a00a99bff4ef88886040516108df929190612a55565b60405180910390a36040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907f742aa8302eca1011d41495047d2d195954b0f91a24ac18e221a1788507e42d7d9060200160405180910390a35050505050505050505050565b5f8281525f5160206131305f395f51905f5260205260409020600101546109688161164b565b6109728383611155565b50505050565b6001600160a01b03811633146109a15760405163334bd91960e11b815260040160405180910390fd5b6109ab8282611655565b505050565b5f5160206130f05f395f51905f526109c78161164b565b5f6109d2848461168e565b90505f6109de82611312565b90508060400151156109fa575f60408201526109fa828261152a565b6040516001600160401b0385169062ffffff8716907f2b140fd22a9c33bfd560c6a50d8f4f235e6d85feeb1629b97b62b488f3c95004905f90a35050505050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7610a658161164b565b610a6d6116d7565b50565b5f5f5f610a7b612548565b5f610a8586611736565b9050610a9081611780565b9450945094509450509193509193565b5f5160206130f05f395f51905f52610ab78161164b565b610ac385858585611197565b5050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d610af48161164b565b610a6d611816565b610b046112ce565b6001600160a01b038116610b2b57604051634434240960e11b815260040160405180910390fd5b610b33612514565b610b3e8185856112fe565b80606001516001600160a01b0316336001600160a01b031614610b74576040516349ad9b0960e11b815260040160405180910390fd5b5f610b7f858561168e565b9050610b8b8184611476565b6040516001600160a01b03841681526001600160401b0385169062ffffff8716907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc906020015b60405180910390a35050505050565b5f610c137ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00546001600160401b031690565b905090565b5f8281525f5160206130d05f395f51905f52602081905260408220610c3d908461185e565b949350505050565b5f9182525f5160206131305f395f51905f52602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f8181525f5160206130d05f395f51905f526020819052604090912060609190610ca490611869565b9392505050565b5f5f5f610cb6612548565b5f610cc1878761168e565b9050610ccc81611780565b929a91995097509095509350505050565b5f5160206130f05f395f51905f52610cf48161164b565b610cfc612514565b610d068186611875565b5f610d108661190b565b85151581526001600160401b03858116602080840191825262ffffff8a165f9081527f250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd1029091526040902083518154925168ffffffffffffffffff1990931690151568ffffffffffffffff00191617610100929093169190910291909117905590506040805186151581526001600160401b038616602082015262ffffff8816917f9ea2c0fdca3eb79272af911e373f2b128f6f8ae84133e610e4798488facc243d910160405180910390a2505050505050565b610deb6112ce565b5f610df533611736565b90505f610e09610e0483611312565b611378565b8051909150610e2b57604051631a8660cb60e01b815260040160405180910390fd5b5f610e358361197a565b9050805f01516001600160401b0316856001600160401b03161180610e6f575080602001516001600160401b0316846001600160401b0316105b80610ea6575080516001600160401b038681169116148015610ea6575080602001516001600160401b0316846001600160401b0316145b15610ec45760405163c7f544bb60e01b815260040160405180910390fd5b610ecc612514565b610ed681856119c8565b610ee28185888861158b565b505050505050565b5f5f5f5f5f610ef76119d9565b80516020820151604083015160609093015191989097509195509350915050565b5f8181525f5160206130d05f395f51905f52602081905260408220610ca490611a6b565b610f446112ce565b5f610f4e33611736565b90505f610f5a82611312565b90505f610f6682611378565b8051909150610f8857604051631a8660cb60e01b815260040160405180910390fd5b8060600151610faa57604051630eedec1160e31b815260040160405180910390fd5b6001600160401b0343166020830152610fc3838361152a565b604080515f81529084901c9084906001600160401b0382169062ffffff8416907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d468490602001610bd2565b5f8281525f5160206131305f395f51905f5260205260409020600101546110338161164b565b6109728383611655565b5f5160206130f05f395f51905f526110548161164b565b5f61105f848461168e565b90505f61106b82611312565b90505f61107782611378565b805190915061109957604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b0343166020830152600160408301526110b9838361152a565b604051600181526001600160401b0386169062ffffff8816907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a3505050505050565b5f6001600160e01b03198216637965db0b60e01b14806104f157506301ffc9a760e01b6001600160e01b03198316146104f1565b611143611a74565b61114b611abd565b565b61114b611a74565b5f5f5160206130d05f395f51905f528161116f8585611add565b90508015610c3d575f85815260208390526040902061118e9085611b7e565b50949350505050565b816001600160401b03165f036111c05760405163ad617a0b60e01b815260040160405180910390fd5b806001600160401b03165f036111e857604051628faa1f60e61b815260040160405180910390fd5b60408051608080820183526001600160401b03878116808452878216602080860182905288841686880181905293881660609687018190527f250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd10380546001600160801b0319168517600160401b8502176fffffffffffffffffffffffffffffffff16600160801b87026001600160c01b031617600160c01b8302179055875193845290830191909152818601929092529283015291517f84d25455db084cf777a0a4a00fcc28946b3a2ebc5cc866ef8820ab8571dbdd36929181900390910190a150505050565b5f5160206131505f395f51905f525460ff161561114b5760405163d93c066560e01b815260040160405180910390fd5b6113088383611875565b6109ab8382611b92565b604080516060810182525f808252602082018190529181019190915261133782611e9d565b60408051606081018252600292909201546001600160401b038082168452600160401b8204166020840152600160801b900460ff1615159082015292915050565b604080516080810182525f808252602082018190529181018290526060810191909152435f6113a56119d9565b90505f5f85602001516001600160401b03161180156113e85750826001600160401b0316826020015186602001516113dd9190612a97565b6001600160401b0316105b90505f5f865f01516001600160401b0316118015611404575081155b90505f8115801561141757508660400151155b90505f8280156114455750845188516001600160401b0388169161143a91612a97565b6001600160401b0316105b6040805160808101825294151585529415156020850152911515938301939093521515606082015295945050505050565b6114808282611ecc565b5f61148a83611e9d565b80549091506001600160a01b031680158015906114b95750826001600160a01b0316816001600160a01b031614155b156114e3576001600160a01b0381165f9081525f5160206131105f395f51905f5260205260408120555b50805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b039290921691821790555f9081525f5160206131105f395f51905f526020526040902055565b8061153483611e9d565b8151600291909101805460208401516040909401511515600160801b0260ff60801b196001600160401b03958616600160401b026001600160801b0319909316959094169490941717919091169190911790555050565b61159a84608001518383611f1d565b83516115a7908383611fa1565b604080518082019091526001600160401b038084168252821660208201526115d0908490612054565b604080850151855182516001600160401b03868116825285811660208301529092169262ffffff909116917f4758feb40e053085c8bfb6815d2061a4057e0820d0f681d7c19609673db27e19910160405180910390a350505050565b8061163683611e9d565b8151600391909101908190610ac39082612b46565b610a6d813361209b565b5f5f5160206130d05f395f51905f528161166f85856120dc565b90508015610c3d575f85815260208390526040902061118e9085612155565b6001600160401b038116604083901b62ffffff60401b16175f6116b082612169565b6001600160a01b0316036104f1576040516325ec6c1f60e01b815260040160405180910390fd5b6116df612183565b5f5160206131505f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b6001600160a01b0381165f9081525f5160206131105f395f51905f5260205260408120549081900361177b5760405163fe0a2bb160e01b815260040160405180910390fd5b919050565b5f5f5f61178b612548565b611793612514565b61179d81876119c8565b6117a6866121b2565b91505f6117b68360400151611378565b90505f6117c5835f015161190b565b825190915080156117d557508051155b80156117e257508260a001515b80156117fd57505f5160206131505f395f51905f525460ff16155b9450825f01518360400151965096505050509193509193565b61181e6112ce565b5f5160206131505f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833611718565b5f610ca483836122ee565b60605f610ca483612314565b5f61187e61236d565b604051630bc1bb1960e41b815262ffffff841660048201526001600160a01b03919091169063bc1bb190906024015f60405180830381865afa1580156118c6573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526118ed9190810190612d17565b62ffffff9092168352506020908101516001600160a01b0316910152565b6040805180820182525f808252602091820181905262ffffff9390931683527f250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd10281529181902081518083019092525460ff81161515825261010090046001600160401b03169181019190915290565b604080518082019091525f808252602082015261199682611e9d565b60408051808201909152600191909101546001600160401b038082168352600160401b90910416602082015292915050565b604081901c816109728483836112fe565b604080516080810182525f8082526020820181905291810182905260608101919091527f250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd10060408051608081018252600392909201546001600160401b038082168452600160401b820481166020850152600160801b8204811692840192909252600160c01b9004166060820152919050565b5f6104f1825490565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661114b57604051631afcd79f60e31b815260040160405180910390fd5b611ac5611a74565b5f5160206131505f395f51905f52805460ff19169055565b5f5f5160206131305f395f51905f52611af68484610c45565b611b75575f848152602082815260408083206001600160a01b03871684529091529020805460ff19166001179055611b2b3390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506104f1565b5f9150506104f1565b5f610ca4836001600160a01b0384166123ee565b815162ffffff165f03611bb857604051634632571560e01b815260040160405180910390fd5b5f82602001516001600160a01b031663a70c70e46040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bf9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c1d9190612e48565b9050806001600160401b0316826001600160401b031610611c515760405163b9aa612760e01b815260040160405180910390fd5b5f83602001516001600160a01b03166315dae03e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c92573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cb69190612e48565b90507f00000000000000000000000000000000000000000000000000000000000000008103611df4576020840151604051630bc5f72160e31b81526001600160401b03851660048201526001600160a01b03821690635e2fb90890602401602060405180830381865afa158015611d2f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d539190612e6a565b151560a08601526040516365c14dc760e01b81526001600160401b03851660048201525f906001600160a01b038316906365c14dc7906024016101e060405180830381865afa158015611da8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dcc9190612e98565b6101808101516001600160a01b031660608801525163ffffffff166080870152506109729050565b6020840151604051632695a60f60e21b81526001600160401b03851660048201525f60248201526001600160a01b03821690639a56983c906044015f60405180830381865afa158015611e49573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611e709190810190612fc1565b6001600160401b031660808b015250506001600160a01b0316606088015250151560a08601525050505050565b5f9081527f250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd1006020526040902090565b6001600160a01b0381165f9081525f5160206131105f395f51905f5260205260409020548015801590611eff5750828114155b156109ab5760405163324e8e5f60e01b815260040160405180910390fd5b806001600160401b0316826001600160401b03161115611f505760405163c7f544bb60e01b815260040160405180910390fd5b826001600160401b0316816001600160401b0316101580611f835750826001600160401b0316826001600160401b031610155b156109ab57604051637ba485c560e01b815260040160405180910390fd5b5f611faa6119d9565b90505f611fb68561190b565b805190915015611fd957604051630dbfe5fd60e31b815260040160405180910390fd5b5f611fe4858561305a565b611fef906001612a97565b90505f82602001516001600160401b03165f14612010578260200151612016565b83604001515b9050806001600160401b0316826001600160401b0316111561204b57604051631d1f75fb60e31b815260040160405180910390fd5b50505050505050565b8061205e83611e9d565b815160019190910180546020909301516001600160401b03908116600160401b026001600160801b03199094169216919091179190911790555050565b6120a58282610c45565b6120d85760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b5f5f5160206131305f395f51905f526120f58484610c45565b15611b75575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506104f1565b5f610ca4836001600160a01b03841661243a565b5f61217382611e9d565b546001600160a01b031692915050565b5f5160206131505f395f51905f525460ff1661114b57604051638dfc202b60e01b815260040160405180910390fd5b6121ba612548565b6121c382611e9d565b6040805160808101825282546001600160a01b031681528151808301835260018401546001600160401b038082168352600160401b918290048116602080850191909152808501939093528451606080820187526002880154808416835293840490921681850152600160801b90920460ff161515828601528385019190915283519182019093526003840180549294938501928290829061226490612aca565b80601f016020809104026020016040519081016040528092919081815260200182805461229090612aca565b80156122db5780601f106122b2576101008083540402835291602001916122db565b820191905f5260205f20905b8154815290600101906020018083116122be57829003601f168201915b5050509190925250505090525092915050565b5f825f01828154811061230357612303613079565b905f5260205f200154905092915050565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561236157602002820191905f5260205f20905b81548152602001906001019080831161234d575b50505050509050919050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123ca573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c13919061308d565b5f81815260018301602052604081205461243357508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556104f1565b505f6104f1565b5f8181526001830160205260408120548015611b75575f61245c6001836130a8565b85549091505f9061246f906001906130a8565b90508082146124ce575f865f01828154811061248d5761248d613079565b905f5260205f200154905080875f0184815481106124ad576124ad613079565b5f918252602080832090910192909255918252600188019052604090208390555b85548690806124df576124df6130bb565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506104f1565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180608001604052805f6001600160a01b0316815260200161258f60405180604001604052805f6001600160401b031681526020015f6001600160401b031681525090565b8152604080516060810182525f80825260208281018290529282015291019081526020016125c96040518060200160405280606081525090565b905290565b5f602082840312156125de575f5ffd5b81356001600160e01b031981168114610ca4575f5ffd5b6001600160a01b0381168114610a6d575f5ffd5b6001600160401b0381168114610a6d575f5ffd5b5f5f5f5f5f60a08688031215612631575f5ffd5b853561263c816125f5565b9450602086013561264c81612609565b9350604086013561265c81612609565b9250606086013561266c81612609565b9150608086013561267c81612609565b809150509295509295909350565b5f6020828403121561269a575f5ffd5b5035919050565b62ffffff81168114610a6d575f5ffd5b5f5f5f5f5f5f5f60c0888a0312156126c7575f5ffd5b87356126d2816126a1565b965060208801356126e281612609565b955060408801356126f2816125f5565b9450606088013561270281612609565b9350608088013561271281612609565b925060a08801356001600160401b0381111561272c575f5ffd5b8801601f81018a1361273c575f5ffd5b80356001600160401b03811115612751575f5ffd5b8a6020828401011115612762575f5ffd5b602082019350809250505092959891949750929550565b5f5f6040838503121561278a575f5ffd5b82359150602083013561279c816125f5565b809150509250929050565b5f5f604083850312156127b8575f5ffd5b82356127c3816126a1565b9150602083013561279c81612609565b5f602082840312156127e3575f5ffd5b8135610ca4816125f5565b5f81516020845280518060208601528060208301604087015e5f604082870101526040601f19601f8301168601019250505092915050565b62ffffff851681526001600160401b03841660208201528215156040820152608060608201526001600160a01b0382511660808201525f60208301516001600160401b0381511660a08401526001600160401b0360208201511660c08401525060408301516001600160401b0381511660e08401526001600160401b036020820151166101008401526040810151151561012084015250606083015160e06101408401526128d86101608401826127ee565b979650505050505050565b5f5f5f5f608085870312156128f6575f5ffd5b843561290181612609565b9350602085013561291181612609565b9250604085013561292181612609565b9150606085013561293181612609565b939692955090935050565b5f5f5f6060848603121561294e575f5ffd5b8335612959816126a1565b9250602084013561296981612609565b91506040840135612979816125f5565b809150509250925092565b5f5f60408385031215612995575f5ffd5b50508035926020909101359150565b602080825282518282018190525f918401906040840190835b818110156129e45783516001600160a01b03168352602093840193909201916001016129bd565b509095945050505050565b8015158114610a6d575f5ffd5b5f5f5f60608486031215612a0e575f5ffd5b8335612a19816126a1565b92506020840135612a29816129ef565b9150604084013561297981612609565b5f5f60408385031215612a4a575f5ffd5b82356127c381612609565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b0381811683821601908111156104f1576104f1612a83565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680612ade57607f821691505b602082108103612afc57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156109ab57805f5260205f20601f840160051c81016020851015612b275750805b601f840160051c820191505b81811015610ac3575f8155600101612b33565b81516001600160401b03811115612b5f57612b5f612ab6565b612b7381612b6d8454612aca565b84612b02565b6020601f821160018114612ba5575f8315612b8e5750848201515b5f19600385901b1c1916600184901b178455610ac3565b5f84815260208120601f198516915b82811015612bd45787850151825560209485019460019092019101612bb4565b5084821015612bf157868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6040516101a081016001600160401b0381118282101715612c2357612c23612ab6565b60405290565b6040516101e081016001600160401b0381118282101715612c2357612c23612ab6565b805161177b816126a1565b805161177b816125f5565b805161ffff8116811461177b575f5ffd5b805160ff8116811461177b575f5ffd5b5f82601f830112612c92575f5ffd5b81516001600160401b03811115612cab57612cab612ab6565b604051601f8201601f19908116603f011681016001600160401b0381118282101715612cd957612cd9612ab6565b604052818152838201602001851015612cf0575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b805161177b81612609565b5f60208284031215612d27575f5ffd5b81516001600160401b03811115612d3c575f5ffd5b82016101a08185031215612d4e575f5ffd5b612d56612c00565b612d5f82612c4c565b8152612d6d60208301612c57565b6020820152612d7e60408301612c62565b6040820152612d8f60608301612c62565b6060820152612da060808301612c62565b6080820152612db160a08301612c73565b60a082015260c08201516001600160401b03811115612dce575f5ffd5b612dda86828501612c83565b60c083015250612dec60e08301612d0c565b60e082015261010082810151908201526101208083015190820152612e146101408301612c62565b610140820152612e276101608301612d0c565b610160820152612e3a6101808301612d0c565b610180820152949350505050565b5f60208284031215612e58575f5ffd5b5051919050565b805161177b816129ef565b5f60208284031215612e7a575f5ffd5b8151610ca4816129ef565b805163ffffffff8116811461177b575f5ffd5b5f6101e0828403128015612eaa575f5ffd5b50612eb3612c29565b612ebc83612e85565b8152612eca60208401612e85565b6020820152612edb60408401612e85565b6040820152612eec60608401612e85565b6060820152612efd60808401612e85565b6080820152612f0e60a08401612e85565b60a0820152612f1f60c08401612e85565b60c0820152612f3060e08401612c73565b60e0820152612f426101008401612e85565b610100820152612f556101208401612e85565b610120820152612f686101408401612c57565b610140820152612f7b6101608401612c57565b610160820152612f8e6101808401612c57565b610180820152612fa16101a08401612c57565b6101a0820152612fb46101c08401612e5f565b6101c08201529392505050565b5f5f5f5f5f5f60c08789031215612fd6575f5ffd5b8651612fe1816129ef565b60208801519096506001600160401b03811115612ffc575f5ffd5b61300889828a01612c83565b9550506040870151613019816125f5565b606088015190945061302a81612609565b608088015190935061303b81612609565b60a088015190925061304c81612609565b809150509295509295509295565b6001600160401b0382811682821603908111156104f1576104f1612a83565b634e487b7160e01b5f52603260045260245ffd5b5f6020828403121561309d575f5ffd5b8151610ca4816125f5565b818103818111156104f1576104f1612a83565b634e487b7160e01b5f52603160045260245ffdfec1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000794daa56950487582951e8db2fdbcbee68c2223c65641d0aa02a3afc64f9a86f250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd10102dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a164736f6c634300081c000a00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000", + "nonce": "0x3d", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionType": "CREATE", + "contractName": "OssifiableProxy", + "contractAddress": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "function": null, + "arguments": [ + "0x73CCA4DD9E58Fa3aBCA22d55ee81AE613d958980", + "0x401FD888B5E41113B7c0C47725A742bbc3A083EF", + "0x13e09453000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000f4240" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "gas": "0x139d27", + "value": "0x0", + "input": "0x608060405234801561000f575f5ffd5b50604051610c46380380610c4683398101604081905261002e9161031f565b828161003a828261004e565b506100469050826100ac565b505050610406565b61005782610119565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156100a05761009b8282610197565b505050565b6100a861020a565b5050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6100eb5f516020610c265f395f51905f52546001600160a01b031690565b604080516001600160a01b03928316815291841660208301520160405180910390a16101168161022b565b50565b806001600160a01b03163b5f0361015357604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b60605f5f846001600160a01b0316846040516101b391906103f0565b5f60405180830381855af49150503d805f81146101eb576040519150601f19603f3d011682016040523d82523d5f602084013e6101f0565b606091505b509092509050610201858383610268565b95945050505050565b34156102295760405163b398979f60e01b815260040160405180910390fd5b565b6001600160a01b03811661025457604051633173bdd160e11b81525f600482015260240161014a565b805f516020610c265f395f51905f52610176565b60608261027d57610278826102c7565b6102c0565b815115801561029457506001600160a01b0384163b155b156102bd57604051639996b31560e01b81526001600160a01b038516600482015260240161014a565b50805b9392505050565b8051156102d75780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b80516001600160a01b0381168114610306575f5ffd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f5f5f60608486031215610331575f5ffd5b61033a846102f0565b9250610348602085016102f0565b60408501519092506001600160401b03811115610363575f5ffd5b8401601f81018613610373575f5ffd5b80516001600160401b0381111561038c5761038c61030b565b604051601f8201601f19908116603f011681016001600160401b03811182821017156103ba576103ba61030b565b6040528181528282016020018810156103d1575f5ffd5b8160208401602083015e5f602083830101528093505050509250925092565b5f82518060208501845e5f920191825250919050565b610813806104135f395ff3fe608060405260043610610073575f3560e01c8063916f1fd71161004d578063916f1fd7146100f1578063aad188681461011d578063ad729a711461013c578063adcbc2371461015057610082565b8063133512581461008a5780633ebdd0eb146100b3578063773f5be8146100d257610082565b3661008257610080610164565b005b610080610164565b348015610095575f5ffd5b5061009e610176565b60405190151581526020015b60405180910390f35b3480156100be575f5ffd5b506100806100cd366004610759565b61018f565b3480156100dd575f5ffd5b506100806100ec366004610759565b610206565b3480156100fc575f5ffd5b5061010561026a565b6040516001600160a01b0390911681526020016100aa565b348015610128575f5ffd5b50610080610137366004610772565b610278565b348015610147575f5ffd5b50610105610318565b34801561015b575f5ffd5b50610080610321565b61017461016f610426565b61042f565b565b5f8061018061044d565b6001600160a01b031614905090565b5f61019861044d565b90506001600160a01b0381166101c15760405163b83646a960e01b815260040160405180910390fd5b6001600160a01b03811633146101ea57604051637bfa4b9f60e01b815260040160405180910390fd5b6102028260405180602001604052805f81525061047f565b5050565b5f61020f61044d565b90506001600160a01b0381166102385760405163b83646a960e01b815260040160405180910390fd5b6001600160a01b038116331461026157604051637bfa4b9f60e01b815260040160405180910390fd5b610202826104d9565b5f61027361044d565b905090565b5f61028161044d565b90506001600160a01b0381166102aa5760405163b83646a960e01b815260040160405180910390fd5b6001600160a01b03811633146102d357604051637bfa4b9f60e01b815260040160405180910390fd5b6103128484848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061047f92505050565b50505050565b5f610273610426565b5f61032a61044d565b90506001600160a01b0381166103535760405163b83646a960e01b815260040160405180910390fd5b6001600160a01b038116331461037c57604051637bfa4b9f60e01b815260040160405180910390fd5b5f61038561044d565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610380546001600160a01b0319169055604080516001600160a01b03831681525f602082015281519293507f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f929081900390910190a16040517f158b204828f9326d9bb3c2be9336986c14911b4a72b93d1801f207aac3c68b9f905f90a15050565b5f610273610530565b365f5f375f5f365f845af43d5f5f3e808015610449573d5ff35b3d5ffd5b5f7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b61048882610557565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156104d1576104cc82826105d5565b505050565b610202610647565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61050261044d565b604080516001600160a01b03928316815291841660208301520160405180910390a161052d81610666565b50565b5f7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610470565b806001600160a01b03163b5f0361059157604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b60605f5f846001600160a01b0316846040516105f191906107f0565b5f60405180830381855af49150503d805f8114610629576040519150601f19603f3d011682016040523d82523d5f602084013e61062e565b606091505b509150915061063e8583836106b6565b95945050505050565b34156101745760405163b398979f60e01b815260040160405180910390fd5b6001600160a01b03811661068f57604051633173bdd160e11b81525f6004820152602401610588565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036105b4565b6060826106cb576106c682610715565b61070e565b81511580156106e257506001600160a01b0384163b155b1561070b57604051639996b31560e01b81526001600160a01b0385166004820152602401610588565b50805b9392505050565b8051156107255780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b80356001600160a01b0381168114610754575f5ffd5b919050565b5f60208284031215610769575f5ffd5b61070e8261073e565b5f5f5f60408486031215610784575f5ffd5b61078d8461073e565b9250602084013567ffffffffffffffff8111156107a8575f5ffd5b8401601f810186136107b8575f5ffd5b803567ffffffffffffffff8111156107ce575f5ffd5b8660208284010111156107df575f5ffd5b939660209190910195509293505050565b5f82518060208501845e5f92019182525091905056fea164736f6c634300081c000ab53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610300000000000000000000000073cca4dd9e58fa3abca22d55ee81ae613d958980000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a413e09453000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000", + "nonce": "0x3e", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x138cad03fbe5a1d4f10a6765b4328249858670248d32bb3f8fbbde49ac17a339", + "transactionType": "CREATE", + "contractName": "CCR", + "contractAddress": "0x7c178b9b797c6ea6776a784c22a0f95a79385c9b", + "function": null, + "arguments": [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "gas": "0x386e1c", + "value": "0x0", + "input": "0x610100604052348015610010575f5ffd5b506040516134e03803806134e083398101604081905261002f91610245565b6040517f6c69646f2e636363702e73746f726167652e436f6e66696753746f7261676500602082015260ff1990600190603f01604051602081830303815290604052805190602001205f1c610084919061027c565b60405160200161009691815260200190565b60408051601f198184030181529082905280516020918201209290921660805260ff19916001916100fb91017f6c69646f2e636363702e73746f726167652e4f70657261746f7253746174657381526653746f7261676560c81b602082015260270190565b604051602081830303815290604052805190602001205f1c61011d919061027c565b60405160200161012f91815260200190565b60408051601f1981840301815291905280516020909101201660a0526001600160a01b03821661017257604051630f05a38b60e41b815260040160405180910390fd5b6001600160a01b03821660c05260e081905261018c610193565b50506102a1565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156101e35760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146102425780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f5f60408385031215610256575f5ffd5b82516001600160a01b038116811461026c575f5ffd5b6020939093015192949293505050565b8181038181111561029b57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e0516131d461030c5f395f611d0c01525f818161049801526123e801525f81816113b401528181611459015281816114b4015281816116d70152611ef501525f81816110670152818161188201528181611b42015261230f01526131d45ff3fe608060405234801561000f575f5ffd5b50600436106101d1575f3560e01c80638aa10435116100fe578063ba6bc8d51161009e578063d547741f1161006e578063d547741f1461046d578063d6dc6ae714610480578063dbba4b4814610493578063fc3372f5146104ba575f5ffd5b8063ba6bc8d514610404578063c3f909d414610417578063ca15c87314610452578063d4eec5a614610465575f5ffd5b8063a217fddf116100d9578063a217fddf146103b6578063a3246ad3146103bd578063a3714e07146103dd578063ad32563d146103f0575f5ffd5b80638aa10435146103585780639010d07c1461037857806391d14854146103a3575f5ffd5b8063389ed267116101745780635c975abb116101445780635c975abb1461031357806370bc09f61461032a5780638456cb591461033d578063893d004e14610345575f5ffd5b8063389ed267146102ae5780633c074eb3146102d55780633f4ba83a146102e85780635865c60c146102f0575f5ffd5b80632de03aa1116101af5780632de03aa11461024e5780632de0920c146102755780632f2ff15d1461028857806336568abe1461029b575f5ffd5b806301ffc9a7146101d557806313e09453146101fd578063248a9ca314610212575b5f5ffd5b6101e86101e3366004612646565b6104cd565b60405190151581526020015b60405180910390f35b61021061020b366004612695565b6104f7565b005b610240610220366004612702565b5f9081525f5160206131885f395f51905f52602052604090206001015490565b6040519081526020016101f4565b6102407f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b610210610283366004612729565b6106ba565b6102106102963660046127f1565b61094a565b6102106102a93660046127f1565b610980565b6102407f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b6102106102e336600461281f565b6109b8565b610210610a43565b6103036102fe36600461284b565b610a78565b6040516101f4949392919061289e565b5f5160206131a85f395f51905f525460ff166101e8565b61021061033836600461295b565b610aa8565b610210610ad2565b6102106103533660046129b4565b610b04565b610360610bf2565b6040516001600160401b0390911681526020016101f4565b61038b6103863660046129fc565b610c29565b6040516001600160a01b0390911681526020016101f4565b6101e86103b13660046127f1565b610c56565b6102405f81565b6103d06103cb366004612702565b610c8c565b6040516101f49190612a1c565b6103036103eb36600461281f565b610cbc565b6102405f5160206131685f395f51905f5281565b610210610412366004612a67565b610cee565b61041f610dfe565b604080516001600160401b03958616815293851660208501529184169183019190915290911660608201526080016101f4565b610240610460366004612702565b610e18565b610210610e3c565b61021061047b3660046127f1565b610f17565b61021061048e36600461281f565b610f47565b61038b7f000000000000000000000000000000000000000000000000000000000000000081565b6102106104c8366004612a90565b611011565b5f6001600160e01b03198216635a05180f60e01b14806104f157506104f182611103565b92915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f8115801561053b5750825b90505f826001600160401b031660011480156105565750303b155b905081158015610564575080155b156105825760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156105ac57845460ff60401b1916600160401b1785555b6001600160a01b038a166105d3576040516319467aa760e11b815260040160405180910390fd5b6105db611137565b6105e3611149565b6105ed5f8b611151565b506106055f5160206131685f395f51905f528b611151565b506106307f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d8b611151565b5061065b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c78b611151565b5061066889898989611193565b83156106ae57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b6106c26111f8565b6106ca61258c565b6001600160a01b0386166106f157604051634434240960e11b815260040160405180910390fd5b6106fc818989611228565b80606001516001600160a01b0316336001600160a01b031614610732576040516349ad9b0960e11b815260040160405180910390fd5b8060a0015161075457604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b038716604089901b62ffffff60401b16175f6107768261123c565b90505f610782826112a2565b8051909150156107a5576040516342ee68b560e01b815260040160405180910390fd5b80604001516107c7576040516328a5715760e21b815260040160405180910390fd5b6107d1838a6113a2565b6040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc9060200160405180910390a3610857836040518060600160405280436001600160401b031681526020015f6001600160401b031681526020015f15158152506114e0565b61086384848a8a611541565b604080516020601f8801819004810282018301835281018781526108a69286929182918b908b90819085018382808284375f9201919091525050509152506115bb565b896001600160401b03168b62ffffff167f8ae0affaa3c6c49aee34fd262b42a1ed56bf92a700857bddf013a00a99bff4ef88886040516108e7929190612acd565b60405180910390a36040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907f742aa8302eca1011d41495047d2d195954b0f91a24ac18e221a1788507e42d7d9060200160405180910390a35050505050505050505050565b5f8281525f5160206131885f395f51905f526020526040902060010154610970816115da565b61097a8383611151565b50505050565b6001600160a01b03811633146109a95760405163334bd91960e11b815260040160405180910390fd5b6109b382826115e4565b505050565b5f5160206131685f395f51905f526109cf816115da565b5f6109da848461161d565b90505f6109e68261123c565b9050806040015115610a02575f6040820152610a0282826114e0565b6040516001600160401b0385169062ffffff8716907f2b140fd22a9c33bfd560c6a50d8f4f235e6d85feeb1629b97b62b488f3c95004905f90a35050505050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7610a6d816115da565b610a75611666565b50565b5f5f5f610a836125c0565b5f610a8d866116c5565b9050610a9881611725565b9450945094509450509193509193565b5f5160206131685f395f51905f52610abf816115da565b610acb85858585611193565b5050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d610afc816115da565b610a756117c7565b610b0c6111f8565b6001600160a01b038116610b3357604051634434240960e11b815260040160405180910390fd5b610b3b61258c565b610b46818585611228565b80606001516001600160a01b0316336001600160a01b031614610b7c576040516349ad9b0960e11b815260040160405180910390fd5b610b9d62ffffff60401b604086901b166001600160401b03851617836113a2565b6040516001600160a01b03831681526001600160401b0384169062ffffff8616907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc906020015b60405180910390a350505050565b5f610c247ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00546001600160401b031690565b905090565b5f8281525f5160206131485f395f51905f52602081905260408220610c4e908461180f565b949350505050565b5f9182525f5160206131885f395f51905f52602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f8181525f5160206131485f395f51905f526020819052604090912060609190610cb59061181a565b9392505050565b5f5f5f610cc76125c0565b5f610cd2878761161d565b9050610cdd81611725565b929a91995097509095509350505050565b610cf66111f8565b5f610d00336116c5565b90505f610d14610d0f8361123c565b6112a2565b8051909150610d3657604051631a8660cb60e01b815260040160405180910390fd5b5f610d4083611826565b9050805f01516001600160401b0316856001600160401b03161180610d7a575080602001516001600160401b0316846001600160401b0316105b80610db1575080516001600160401b038681169116148015610db1575080602001516001600160401b0316846001600160401b0316145b15610dcf5760405163c7f544bb60e01b815260040160405180910390fd5b610dd761258c565b604084901c84610de8838383611228565b610df483878a8a611541565b5050505050505050565b5f5f5f5f610e0a611874565b935093509350935090919293565b5f8181525f5160206131485f395f51905f52602081905260408220610cb5906118ea565b610e446111f8565b5f610e4e336116c5565b90505f610e5a8261123c565b90505f610e66826112a2565b8051909150610e8857604051631a8660cb60e01b815260040160405180910390fd5b8060600151610eaa57604051630eedec1160e31b815260040160405180910390fd5b6001600160401b0343166020830152610ec383836114e0565b604080515f81529084901c9084906001600160401b0382169062ffffff8416907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a35050505050565b5f8281525f5160206131885f395f51905f526020526040902060010154610f3d816115da565b61097a83836115e4565b5f5160206131685f395f51905f52610f5e816115da565b5f610f69848461161d565b90505f610f758261123c565b90505f610f81826112a2565b8051909150610fa357604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b034316602083015260016040830152610fc383836114e0565b604051600181526001600160401b0386169062ffffff8816907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a3505050505050565b5f5160206131685f395f51905f52611028816115da565b61103061258c565b61103a81866118f3565b6040805180820182526001600160401b038086168252861515602080840191825262ffffff8a165f9081527f0000000000000000000000000000000000000000000000000000000000000000909152939093209151825493511515600160401b0268ffffffffffffffffff19909416911617919091179055604080516001600160401b0385168152851515602082015262ffffff8716917f55bd0114bd34303292838dd2bc70c974c9eea7858dc4fb3716662d439ec3ae12910160405180910390a25050505050565b5f6001600160e01b03198216637965db0b60e01b14806104f157506301ffc9a760e01b6001600160e01b03198316146104f1565b61113f611989565b6111476119d2565b565b611147611989565b5f5f5160206131485f395f51905f528161116b85856119f2565b90508015610c4e575f85815260208390526040902061118a9085611a93565b50949350505050565b61119f84848484611aa7565b604080516001600160401b0386811682528581166020830152848116828401528316606082015290517f84d25455db084cf777a0a4a00fcc28946b3a2ebc5cc866ef8820ab8571dbdd369181900360800190a150505050565b5f5160206131a85f395f51905f525460ff16156111475760405163d93c066560e01b815260040160405180910390fd5b61123283836118f3565b6109b38382611be4565b604080516060810182525f808252602082018190529181019190915261126182611eef565b60408051606081018252600292909201546001600160401b038082168452600160401b8204166020840152600160801b900460ff1615159082015292915050565b604080516080810182525f808252602082018190529181018290526060810191909152435f806112d0611874565b5050915091505f5f86602001516001600160401b03161180156113135750836001600160401b03168287602001516113089190612b0f565b6001600160401b0316105b90505f5f875f01516001600160401b031611801561132f575081155b90505f8115801561134257508760400151155b90505f828015611370575088516001600160401b03881690611365908890612b0f565b6001600160401b0316105b604080516080810182529415158552941515602085015291151593830193909352151560608201529695505050505050565b6001600160a01b0381165f90815260017f000000000000000000000000000000000000000000000000000000000000000001602052604090205480158015906113eb5750828114155b156114095760405163324e8e5f60e01b815260040160405180910390fd5b5f61141384611eef565b80549091506001600160a01b031680158015906114425750836001600160a01b0316816001600160a01b031614155b15611482576001600160a01b0381165f90815260017f00000000000000000000000000000000000000000000000000000000000000000160205260408120555b50805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03939093169283179055505f9081527f00000000000000000000000000000000000000000000000000000000000000006001016020526040902055565b806114ea83611eef565b8151600291909101805460208401516040909401511515600160801b0260ff60801b196001600160401b03958616600160401b026001600160801b0319909316959094169490941717919091169190911790555050565b61155084608001518383611f1e565b835161155d908383611fa2565b611568838383612043565b604080850151855182516001600160401b03868116825285811660208301529092169262ffffff909116917f9841e87ed1a3c39f013566d8bc2c2119007cfb8c591ccde84c6e012e8891410c9101610be4565b806115c583611eef565b8151600391909101908190610acb9082612bbe565b610a7581336120b1565b5f5f5160206131485f395f51905f52816115fe85856120f2565b90508015610c4e575f85815260208390526040902061118a908561216b565b6001600160401b038116604083901b62ffffff60401b16175f61163f8261217f565b6001600160a01b0316036104f1576040516325ec6c1f60e01b815260040160405180910390fd5b61166e612199565b5f5160206131a85f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b6001600160a01b0381165f90815260017f0000000000000000000000000000000000000000000000000000000000000000016020526040812054908190036117205760405163fe0a2bb160e01b815260040160405180910390fd5b919050565b5f5f5f6117306125c0565b61173861258c565b604086901c945085935061174d818686611228565b611756866121c8565b91505f61176683604001516112a2565b90505f611775835f0151612304565b915050815f01518015611786575080155b801561179357508260a001515b80156117ae57505f5160206131a85f395f51905f525460ff16155b9450825f01518360400151965096505050509193509193565b6117cf6111f8565b5f5160206131a85f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258336116a7565b5f610cb58383612366565b60605f610cb58361238c565b604080518082019091525f808252602082015261184282611eef565b60408051808201909152600191909101546001600160401b038082168352600160401b90910416602082015292915050565b6040805160808101825260017f000000000000000000000000000000000000000000000000000000000000000001546001600160401b03808216808452600160401b8304821660208501819052600160801b84048316958501869052600160c01b90930490911660609093018390529390929190565b5f6104f1825490565b5f6118fc6123e5565b604051630bc1bb1960e41b815262ffffff841660048201526001600160a01b03919091169063bc1bb190906024015f60405180830381865afa158015611944573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261196b9190810190612d8f565b62ffffff9092168352506020908101516001600160a01b0316910152565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661114757604051631afcd79f60e31b815260040160405180910390fd5b6119da611989565b5f5160206131a85f395f51905f52805460ff19169055565b5f5f5160206131885f395f51905f52611a0b8484610c56565b611a8a575f848152602082815260408083206001600160a01b03871684529091529020805460ff19166001179055611a403390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506104f1565b5f9150506104f1565b5f610cb5836001600160a01b038416612466565b816001600160401b03165f03611ad05760405163ad617a0b60e01b815260040160405180910390fd5b806001600160401b03165f03611af857604051628faa1f60e61b815260040160405180910390fd5b6040518060800160405280856001600160401b03168152602001846001600160401b03168152602001836001600160401b03168152602001826001600160401b0316815250611b647f000000000000000000000000000000000000000000000000000000000000000090565b81516001919091018054602084015160408501516060909501516001600160401b03908116600160c01b026001600160c01b03968216600160801b02969096166fffffffffffffffffffffffffffffffff928216600160401b026001600160801b0319909416919095161791909117169190911791909117905550505050565b815162ffffff165f03611c0a57604051634632571560e01b815260040160405180910390fd5b5f82602001516001600160a01b031663a70c70e46040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c4b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c6f9190612ec0565b9050806001600160401b0316826001600160401b031610611ca35760405163b9aa612760e01b815260040160405180910390fd5b5f83602001516001600160a01b03166315dae03e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ce4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d089190612ec0565b90507f00000000000000000000000000000000000000000000000000000000000000008103611e46576020840151604051630bc5f72160e31b81526001600160401b03851660048201526001600160a01b03821690635e2fb90890602401602060405180830381865afa158015611d81573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611da59190612ee2565b151560a08601526040516365c14dc760e01b81526001600160401b03851660048201525f906001600160a01b038316906365c14dc7906024016101e060405180830381865afa158015611dfa573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e1e9190612f10565b6101808101516001600160a01b031660608801525163ffffffff1660808701525061097a9050565b6020840151604051632695a60f60e21b81526001600160401b03851660048201525f60248201526001600160a01b03821690639a56983c906044015f60405180830381865afa158015611e9b573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611ec29190810190613039565b6001600160401b031660808b015250506001600160a01b0316606088015250151560a08601525050505050565b5f9081527f00000000000000000000000000000000000000000000000000000000000000006020526040902090565b806001600160401b0316826001600160401b03161115611f515760405163c7f544bb60e01b815260040160405180910390fd5b826001600160401b0316816001600160401b0316101580611f845750826001600160401b0316826001600160401b031610155b156109b357604051637ba485c560e01b815260040160405180910390fd5b5f611fab611874565b50925050505f5f611fbb86612304565b915091508015611fde57604051630dbfe5fd60e31b815260040160405180910390fd5b5f611fe986866130d2565b611ff4906001612b0f565b90505f6001600160401b0384161561200c578361200e565b845b9050806001600160401b0316826001600160401b03161115610df457604051631d1f75fb60e31b815260040160405180910390fd5b6040518060400160405280836001600160401b03168152602001826001600160401b031681525061207384611eef565b815160019190910180546020909301516001600160401b03908116600160401b026001600160801b0319909416921691909117919091179055505050565b6120bb8282610c56565b6120ee5760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b5f5f5160206131885f395f51905f5261210b8484610c56565b15611a8a575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506104f1565b5f610cb5836001600160a01b0384166124b2565b5f61218982611eef565b546001600160a01b031692915050565b5f5160206131a85f395f51905f525460ff1661114757604051638dfc202b60e01b815260040160405180910390fd5b6121d06125c0565b6121d982611eef565b6040805160808101825282546001600160a01b031681528151808301835260018401546001600160401b038082168352600160401b918290048116602080850191909152808501939093528451606080820187526002880154808416835293840490921681850152600160801b90920460ff161515828601528385019190915283519182019093526003840180549294938501928290829061227a90612b42565b80601f01602080910402602001604051908101604052809291908181526020018280546122a690612b42565b80156122f15780601f106122c8576101008083540402835291602001916122f1565b820191905f5260205f20905b8154815290600101906020018083116122d457829003601f168201915b5050509190925250505090525092915050565b62ffffff165f9081527f000000000000000000000000000000000000000000000000000000000000000060209081526040918290208251808401909352546001600160401b038116808452600160401b90910460ff1615159290910182905291565b5f825f01828154811061237b5761237b6130f1565b905f5260205f200154905092915050565b6060815f018054806020026020016040519081016040528092919081815260200182805480156123d957602002820191905f5260205f20905b8154815260200190600101908083116123c5575b50505050509050919050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612442573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c249190613105565b5f8181526001830160205260408120546124ab57508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556104f1565b505f6104f1565b5f8181526001830160205260408120548015611a8a575f6124d4600183613120565b85549091505f906124e790600190613120565b9050808214612546575f865f018281548110612505576125056130f1565b905f5260205f200154905080875f018481548110612525576125256130f1565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061255757612557613133565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506104f1565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180608001604052805f6001600160a01b0316815260200161260760405180604001604052805f6001600160401b031681526020015f6001600160401b031681525090565b8152604080516060810182525f80825260208281018290529282015291019081526020016126416040518060200160405280606081525090565b905290565b5f60208284031215612656575f5ffd5b81356001600160e01b031981168114610cb5575f5ffd5b6001600160a01b0381168114610a75575f5ffd5b6001600160401b0381168114610a75575f5ffd5b5f5f5f5f5f60a086880312156126a9575f5ffd5b85356126b48161266d565b945060208601356126c481612681565b935060408601356126d481612681565b925060608601356126e481612681565b915060808601356126f481612681565b809150509295509295909350565b5f60208284031215612712575f5ffd5b5035919050565b62ffffff81168114610a75575f5ffd5b5f5f5f5f5f5f5f60c0888a03121561273f575f5ffd5b873561274a81612719565b9650602088013561275a81612681565b9550604088013561276a8161266d565b9450606088013561277a81612681565b9350608088013561278a81612681565b925060a08801356001600160401b038111156127a4575f5ffd5b8801601f81018a136127b4575f5ffd5b80356001600160401b038111156127c9575f5ffd5b8a60208284010111156127da575f5ffd5b602082019350809250505092959891949750929550565b5f5f60408385031215612802575f5ffd5b8235915060208301356128148161266d565b809150509250929050565b5f5f60408385031215612830575f5ffd5b823561283b81612719565b9150602083013561281481612681565b5f6020828403121561285b575f5ffd5b8135610cb58161266d565b5f81516020845280518060208601528060208301604087015e5f604082870101526040601f19601f8301168601019250505092915050565b62ffffff851681526001600160401b03841660208201528215156040820152608060608201526001600160a01b0382511660808201525f60208301516001600160401b0381511660a08401526001600160401b0360208201511660c08401525060408301516001600160401b0381511660e08401526001600160401b036020820151166101008401526040810151151561012084015250606083015160e0610140840152612950610160840182612866565b979650505050505050565b5f5f5f5f6080858703121561296e575f5ffd5b843561297981612681565b9350602085013561298981612681565b9250604085013561299981612681565b915060608501356129a981612681565b939692955090935050565b5f5f5f606084860312156129c6575f5ffd5b83356129d181612719565b925060208401356129e181612681565b915060408401356129f18161266d565b809150509250925092565b5f5f60408385031215612a0d575f5ffd5b50508035926020909101359150565b602080825282518282018190525f918401906040840190835b81811015612a5c5783516001600160a01b0316835260209384019390920191600101612a35565b509095945050505050565b5f5f60408385031215612a78575f5ffd5b823561283b81612681565b8015158114610a75575f5ffd5b5f5f5f60608486031215612aa2575f5ffd5b8335612aad81612719565b92506020840135612abd81612a83565b915060408401356129f181612681565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b0381811683821601908111156104f1576104f1612afb565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680612b5657607f821691505b602082108103612b7457634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156109b357805f5260205f20601f840160051c81016020851015612b9f5750805b601f840160051c820191505b81811015610acb575f8155600101612bab565b81516001600160401b03811115612bd757612bd7612b2e565b612beb81612be58454612b42565b84612b7a565b6020601f821160018114612c1d575f8315612c065750848201515b5f19600385901b1c1916600184901b178455610acb565b5f84815260208120601f198516915b82811015612c4c5787850151825560209485019460019092019101612c2c565b5084821015612c6957868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6040516101a081016001600160401b0381118282101715612c9b57612c9b612b2e565b60405290565b6040516101e081016001600160401b0381118282101715612c9b57612c9b612b2e565b805161172081612719565b80516117208161266d565b805161ffff81168114611720575f5ffd5b805160ff81168114611720575f5ffd5b5f82601f830112612d0a575f5ffd5b81516001600160401b03811115612d2357612d23612b2e565b604051601f8201601f19908116603f011681016001600160401b0381118282101715612d5157612d51612b2e565b604052818152838201602001851015612d68575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b805161172081612681565b5f60208284031215612d9f575f5ffd5b81516001600160401b03811115612db4575f5ffd5b82016101a08185031215612dc6575f5ffd5b612dce612c78565b612dd782612cc4565b8152612de560208301612ccf565b6020820152612df660408301612cda565b6040820152612e0760608301612cda565b6060820152612e1860808301612cda565b6080820152612e2960a08301612ceb565b60a082015260c08201516001600160401b03811115612e46575f5ffd5b612e5286828501612cfb565b60c083015250612e6460e08301612d84565b60e082015261010082810151908201526101208083015190820152612e8c6101408301612cda565b610140820152612e9f6101608301612d84565b610160820152612eb26101808301612d84565b610180820152949350505050565b5f60208284031215612ed0575f5ffd5b5051919050565b805161172081612a83565b5f60208284031215612ef2575f5ffd5b8151610cb581612a83565b805163ffffffff81168114611720575f5ffd5b5f6101e0828403128015612f22575f5ffd5b50612f2b612ca1565b612f3483612efd565b8152612f4260208401612efd565b6020820152612f5360408401612efd565b6040820152612f6460608401612efd565b6060820152612f7560808401612efd565b6080820152612f8660a08401612efd565b60a0820152612f9760c08401612efd565b60c0820152612fa860e08401612ceb565b60e0820152612fba6101008401612efd565b610100820152612fcd6101208401612efd565b610120820152612fe06101408401612ccf565b610140820152612ff36101608401612ccf565b6101608201526130066101808401612ccf565b6101808201526130196101a08401612ccf565b6101a082015261302c6101c08401612ed7565b6101c08201529392505050565b5f5f5f5f5f5f60c0878903121561304e575f5ffd5b865161305981612a83565b60208801519096506001600160401b03811115613074575f5ffd5b61308089828a01612cfb565b95505060408701516130918161266d565b60608801519094506130a281612681565b60808801519093506130b381612681565b60a08801519092506130c481612681565b809150509295509295509295565b6001600160401b0382811682821603908111156104f1576104f1612afb565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613115575f5ffd5b8151610cb58161266d565b818103818111156104f1576104f1612afb565b634e487b7160e01b5f52603160045260245ffdfec1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000794daa56950487582951e8db2fdbcbee68c2223c65641d0aa02a3afc64f9a86f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a164736f6c634300081c000a00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000", + "nonce": "0x3f", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xde2a6f7ad4ba810948e3c1e417e3e6520a8828267a594d759ad12d0802fde233", + "transactionType": "CALL", + "contractName": "OssifiableProxy", + "contractAddress": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "function": "proxy__upgradeTo(address)", + "arguments": [ + "0x7c178B9B797C6ea6776A784C22A0f95a79385c9b" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "gas": "0xbcc3", + "value": "0x0", + "input": "0x3ebdd0eb0000000000000000000000007c178b9b797c6ea6776a784c22a0f95a79385c9b", + "nonce": "0x42", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xfb7ba8b9366bd8189498e164cabf5317c199115d94919cbf432de63e185af960", + "transactionType": "CREATE", + "contractName": "CCR", + "contractAddress": "0x0442fb2c8607d04d6ba496ea8990da7bd7ec25fc", + "function": null, + "arguments": [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "gas": "0x387d65", + "value": "0x0", + "input": "0x610100604052348015610010575f5ffd5b506040516134ee3803806134ee83398101604081905261002f91610245565b6040517f6c69646f2e636363702e73746f726167652e436f6e66696753746f7261676500602082015260ff1990600190603f01604051602081830303815290604052805190602001205f1c610084919061027c565b60405160200161009691815260200190565b60408051601f198184030181529082905280516020918201209290921660805260ff19916001916100fb91017f6c69646f2e636363702e73746f726167652e4f70657261746f7253746174657381526653746f7261676560c81b602082015260270190565b604051602081830303815290604052805190602001205f1c61011d919061027c565b60405160200161012f91815260200190565b60408051601f1981840301815291905280516020909101201660a0526001600160a01b03821661017257604051630f05a38b60e41b815260040160405180910390fd5b6001600160a01b03821660c05260e081905261018c610193565b50506102a1565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156101e35760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146102425780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f5f60408385031215610256575f5ffd5b82516001600160a01b038116811461026c575f5ffd5b6020939093015192949293505050565b8181038181111561029b57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e0516131e261030c5f395f611d1a01525f818161049801526123f601525f81816113b401528181611459015281816114b4015281816116d70152611f0301525f81816110670152818161187201528181611b32015261231d01526131e25ff3fe608060405234801561000f575f5ffd5b50600436106101d1575f3560e01c80638aa10435116100fe578063ba6bc8d51161009e578063d547741f1161006e578063d547741f1461046d578063d6dc6ae714610480578063dbba4b4814610493578063fc3372f5146104ba575f5ffd5b8063ba6bc8d514610404578063c3f909d414610417578063ca15c87314610452578063d4eec5a614610465575f5ffd5b8063a217fddf116100d9578063a217fddf146103b6578063a3246ad3146103bd578063a3714e07146103dd578063ad32563d146103f0575f5ffd5b80638aa10435146103585780639010d07c1461037857806391d14854146103a3575f5ffd5b8063389ed267116101745780635c975abb116101445780635c975abb1461031357806370bc09f61461032a5780638456cb591461033d578063893d004e14610345575f5ffd5b8063389ed267146102ae5780633c074eb3146102d55780633f4ba83a146102e85780635865c60c146102f0575f5ffd5b80632de03aa1116101af5780632de03aa11461024e5780632de0920c146102755780632f2ff15d1461028857806336568abe1461029b575f5ffd5b806301ffc9a7146101d557806313e09453146101fd578063248a9ca314610212575b5f5ffd5b6101e86101e3366004612654565b6104cd565b60405190151581526020015b60405180910390f35b61021061020b3660046126a3565b6104f7565b005b610240610220366004612710565b5f9081525f5160206131965f395f51905f52602052604090206001015490565b6040519081526020016101f4565b6102407f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b610210610283366004612737565b6106ba565b6102106102963660046127ff565b61094a565b6102106102a93660046127ff565b610980565b6102407f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b6102106102e336600461282d565b6109b8565b610210610a43565b6103036102fe366004612859565b610a78565b6040516101f494939291906128ac565b5f5160206131b65f395f51905f525460ff166101e8565b610210610338366004612969565b610aa8565b610210610ad2565b6102106103533660046129c2565b610b04565b610360610bf2565b6040516001600160401b0390911681526020016101f4565b61038b610386366004612a0a565b610c29565b6040516001600160a01b0390911681526020016101f4565b6101e86103b13660046127ff565b610c56565b6102405f81565b6103d06103cb366004612710565b610c8c565b6040516101f49190612a2a565b6103036103eb36600461282d565b610cbc565b6102405f5160206131765f395f51905f5281565b610210610412366004612a75565b610cee565b61041f610dfe565b604080516001600160401b03958616815293851660208501529184169183019190915290911660608201526080016101f4565b610240610460366004612710565b610e18565b610210610e3c565b61021061047b3660046127ff565b610f17565b61021061048e36600461282d565b610f47565b61038b7f000000000000000000000000000000000000000000000000000000000000000081565b6102106104c8366004612a9e565b611011565b5f6001600160e01b03198216635a05180f60e01b14806104f157506104f182611103565b92915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f8115801561053b5750825b90505f826001600160401b031660011480156105565750303b155b905081158015610564575080155b156105825760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156105ac57845460ff60401b1916600160401b1785555b6001600160a01b038a166105d3576040516319467aa760e11b815260040160405180910390fd5b6105db611137565b6105e3611149565b6105ed5f8b611151565b506106055f5160206131765f395f51905f528b611151565b506106307f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d8b611151565b5061065b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c78b611151565b5061066889898989611193565b83156106ae57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b6106c26111f8565b6001600160a01b0385166106e957604051634434240960e11b815260040160405180910390fd5b6106f161259a565b6106fc818989611228565b80606001516001600160a01b0316336001600160a01b031614610732576040516349ad9b0960e11b815260040160405180910390fd5b8060a0015161075457604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b038716604089901b62ffffff60401b16175f6107768261123c565b90505f610782826112a2565b8051909150156107a5576040516342ee68b560e01b815260040160405180910390fd5b80604001516107c7576040516328a5715760e21b815260040160405180910390fd5b6107d1838a6113a2565b6040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc9060200160405180910390a3610857836040518060600160405280436001600160401b031681526020015f6001600160401b031681526020015f15158152506114e0565b61086384848a8a611541565b604080516020601f8801819004810282018301835281018781526108a69286929182918b908b90819085018382808284375f9201919091525050509152506115bb565b896001600160401b03168b62ffffff167f8ae0affaa3c6c49aee34fd262b42a1ed56bf92a700857bddf013a00a99bff4ef88886040516108e7929190612adb565b60405180910390a36040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907f742aa8302eca1011d41495047d2d195954b0f91a24ac18e221a1788507e42d7d9060200160405180910390a35050505050505050505050565b5f8281525f5160206131965f395f51905f526020526040902060010154610970816115da565b61097a8383611151565b50505050565b6001600160a01b03811633146109a95760405163334bd91960e11b815260040160405180910390fd5b6109b382826115e4565b505050565b5f5160206131765f395f51905f526109cf816115da565b5f6109da848461161d565b90505f6109e68261123c565b9050806040015115610a02575f6040820152610a0282826114e0565b6040516001600160401b0385169062ffffff8716907f2b140fd22a9c33bfd560c6a50d8f4f235e6d85feeb1629b97b62b488f3c95004905f90a35050505050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7610a6d816115da565b610a75611666565b50565b5f5f5f610a836125ce565b5f610a8d866116c5565b9050610a9881611725565b9450945094509450509193509193565b5f5160206131765f395f51905f52610abf816115da565b610acb85858585611193565b5050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d610afc816115da565b610a756117b7565b610b0c6111f8565b6001600160a01b038116610b3357604051634434240960e11b815260040160405180910390fd5b610b3b61259a565b610b46818585611228565b80606001516001600160a01b0316336001600160a01b031614610b7c576040516349ad9b0960e11b815260040160405180910390fd5b610b9d62ffffff60401b604086901b166001600160401b03851617836113a2565b6040516001600160a01b03831681526001600160401b0384169062ffffff8616907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc906020015b60405180910390a350505050565b5f610c247ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00546001600160401b031690565b905090565b5f8281525f5160206131565f395f51905f52602081905260408220610c4e90846117ff565b949350505050565b5f9182525f5160206131965f395f51905f52602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f8181525f5160206131565f395f51905f526020819052604090912060609190610cb59061180a565b9392505050565b5f5f5f610cc76125ce565b5f610cd2878761161d565b9050610cdd81611725565b929a91995097509095509350505050565b610cf66111f8565b5f610d00336116c5565b90505f610d14610d0f8361123c565b6112a2565b8051909150610d3657604051631a8660cb60e01b815260040160405180910390fd5b5f610d4083611816565b9050805f01516001600160401b0316856001600160401b03161180610d7a575080602001516001600160401b0316846001600160401b0316105b80610db1575080516001600160401b038681169116148015610db1575080602001516001600160401b0316846001600160401b0316145b15610dcf5760405163c7f544bb60e01b815260040160405180910390fd5b610dd761259a565b604084901c84610de8838383611228565b610df483878a8a611541565b5050505050505050565b5f5f5f5f610e0a611864565b935093509350935090919293565b5f8181525f5160206131565f395f51905f52602081905260408220610cb5906118da565b610e446111f8565b5f610e4e336116c5565b90505f610e5a8261123c565b90505f610e66826112a2565b8051909150610e8857604051631a8660cb60e01b815260040160405180910390fd5b8060600151610eaa57604051630eedec1160e31b815260040160405180910390fd5b6001600160401b0343166020830152610ec383836114e0565b604080515f81529084901c9084906001600160401b0382169062ffffff8416907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a35050505050565b5f8281525f5160206131965f395f51905f526020526040902060010154610f3d816115da565b61097a83836115e4565b5f5160206131765f395f51905f52610f5e816115da565b5f610f69848461161d565b90505f610f758261123c565b90505f610f81826112a2565b8051909150610fa357604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b034316602083015260016040830152610fc383836114e0565b604051600181526001600160401b0386169062ffffff8816907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a3505050505050565b5f5160206131765f395f51905f52611028816115da565b61103061259a565b61103a81866118e3565b6040805180820182526001600160401b038086168252861515602080840191825262ffffff8a165f9081527f0000000000000000000000000000000000000000000000000000000000000000909152939093209151825493511515600160401b0268ffffffffffffffffff19909416911617919091179055604080516001600160401b0385168152851515602082015262ffffff8716917f55bd0114bd34303292838dd2bc70c974c9eea7858dc4fb3716662d439ec3ae12910160405180910390a25050505050565b5f6001600160e01b03198216637965db0b60e01b14806104f157506301ffc9a760e01b6001600160e01b03198316146104f1565b61113f611979565b6111476119c2565b565b611147611979565b5f5f5160206131565f395f51905f528161116b85856119e2565b90508015610c4e575f85815260208390526040902061118a9085611a83565b50949350505050565b61119f84848484611a97565b604080516001600160401b0386811682528581166020830152848116828401528316606082015290517f84d25455db084cf777a0a4a00fcc28946b3a2ebc5cc866ef8820ab8571dbdd369181900360800190a150505050565b5f5160206131b65f395f51905f525460ff16156111475760405163d93c066560e01b815260040160405180910390fd5b61123283836118e3565b6109b38382611bd4565b604080516060810182525f808252602082018190529181019190915261126182611efd565b60408051606081018252600292909201546001600160401b038082168452600160401b8204166020840152600160801b900460ff1615159082015292915050565b604080516080810182525f808252602082018190529181018290526060810191909152435f806112d0611864565b5050915091505f5f86602001516001600160401b03161180156113135750836001600160401b03168287602001516113089190612b1d565b6001600160401b0316105b90505f5f875f01516001600160401b031611801561132f575081155b90505f8115801561134257508760400151155b90505f828015611370575088516001600160401b03881690611365908890612b1d565b6001600160401b0316105b604080516080810182529415158552941515602085015291151593830193909352151560608201529695505050505050565b6001600160a01b0381165f90815260017f000000000000000000000000000000000000000000000000000000000000000001602052604090205480158015906113eb5750828114155b156114095760405163324e8e5f60e01b815260040160405180910390fd5b5f61141384611efd565b80549091506001600160a01b031680158015906114425750836001600160a01b0316816001600160a01b031614155b15611482576001600160a01b0381165f90815260017f00000000000000000000000000000000000000000000000000000000000000000160205260408120555b50805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03939093169283179055505f9081527f00000000000000000000000000000000000000000000000000000000000000006001016020526040902055565b806114ea83611efd565b8151600291909101805460208401516040909401511515600160801b0260ff60801b196001600160401b03958616600160401b026001600160801b0319909316959094169490941717919091169190911790555050565b61155084608001518383611f2c565b835161155d908383611fb0565b611568838383612051565b604080850151855182516001600160401b03868116825285811660208301529092169262ffffff909116917f9841e87ed1a3c39f013566d8bc2c2119007cfb8c591ccde84c6e012e8891410c9101610be4565b806115c583611efd565b8151600391909101908190610acb9082612bcc565b610a7581336120bf565b5f5f5160206131565f395f51905f52816115fe8585612100565b90508015610c4e575f85815260208390526040902061118a9085612179565b6001600160401b038116604083901b62ffffff60401b16175f61163f8261218d565b6001600160a01b0316036104f1576040516325ec6c1f60e01b815260040160405180910390fd5b61166e6121a7565b5f5160206131b65f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b6001600160a01b0381165f90815260017f0000000000000000000000000000000000000000000000000000000000000000016020526040812054908190036117205760405163fe0a2bb160e01b815260040160405180910390fd5b919050565b5f5f5f6117306125ce565b61173861259a565b604086901c945085935061174d818686611228565b611756866121d6565b91505f61176683604001516112a2565b90505f61177287612312565b915050815f01518015611783575080155b801561179057508260a001515b80156117ab57505f5160206131b65f395f51905f525460ff16155b94505050509193509193565b6117bf6111f8565b5f5160206131b65f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258336116a7565b5f610cb58383612374565b60605f610cb58361239a565b604080518082019091525f808252602082015261183282611efd565b60408051808201909152600191909101546001600160401b038082168352600160401b90910416602082015292915050565b6040805160808101825260017f000000000000000000000000000000000000000000000000000000000000000001546001600160401b03808216808452600160401b8304821660208501819052600160801b84048316958501869052600160c01b90930490911660609093018390529390929190565b5f6104f1825490565b5f6118ec6123f3565b604051630bc1bb1960e41b815262ffffff841660048201526001600160a01b03919091169063bc1bb190906024015f60405180830381865afa158015611934573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261195b9190810190612d9d565b62ffffff9092168352506020908101516001600160a01b0316910152565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661114757604051631afcd79f60e31b815260040160405180910390fd5b6119ca611979565b5f5160206131b65f395f51905f52805460ff19169055565b5f5f5160206131965f395f51905f526119fb8484610c56565b611a7a575f848152602082815260408083206001600160a01b03871684529091529020805460ff19166001179055611a303390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506104f1565b5f9150506104f1565b5f610cb5836001600160a01b038416612474565b816001600160401b03165f03611ac05760405163ad617a0b60e01b815260040160405180910390fd5b806001600160401b03165f03611ae857604051628faa1f60e61b815260040160405180910390fd5b6040518060800160405280856001600160401b03168152602001846001600160401b03168152602001836001600160401b03168152602001826001600160401b0316815250611b547f000000000000000000000000000000000000000000000000000000000000000090565b81516001919091018054602084015160408501516060909501516001600160401b03908116600160c01b026001600160c01b03968216600160801b02969096166fffffffffffffffffffffffffffffffff928216600160401b026001600160801b0319909416919095161791909117169190911791909117905550505050565b815162ffffff165f03611bfa57604051634632571560e01b815260040160405180910390fd5b5f82602001516001600160a01b031663a70c70e46040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c3b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c5f9190612ece565b9050806001600160401b0316826001600160401b031610611c935760405163b9aa612760e01b815260040160405180910390fd5b8183604001906001600160401b031690816001600160401b0316815250505f83602001516001600160a01b03166315dae03e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cf2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d169190612ece565b90507f00000000000000000000000000000000000000000000000000000000000000008103611e54576020840151604051630bc5f72160e31b81526001600160401b03851660048201526001600160a01b03821690635e2fb90890602401602060405180830381865afa158015611d8f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611db39190612ef0565b151560a08601526040516365c14dc760e01b81526001600160401b03851660048201525f906001600160a01b038316906365c14dc7906024016101e060405180830381865afa158015611e08573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e2c9190612f1e565b6101808101516001600160a01b031660608801525163ffffffff1660808701525061097a9050565b6020840151604051632695a60f60e21b81526001600160401b03851660048201525f60248201526001600160a01b03821690639a56983c906044015f60405180830381865afa158015611ea9573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611ed09190810190613047565b6001600160401b031660808b015250506001600160a01b0316606088015250151560a08601525050505050565b5f9081527f00000000000000000000000000000000000000000000000000000000000000006020526040902090565b806001600160401b0316826001600160401b03161115611f5f5760405163c7f544bb60e01b815260040160405180910390fd5b826001600160401b0316816001600160401b0316101580611f925750826001600160401b0316826001600160401b031610155b156109b357604051637ba485c560e01b815260040160405180910390fd5b5f611fb9611864565b50925050505f5f611fc986612312565b915091508015611fec57604051630dbfe5fd60e31b815260040160405180910390fd5b5f611ff786866130e0565b612002906001612b1d565b90505f6001600160401b0384161561201a578361201c565b845b9050806001600160401b0316826001600160401b03161115610df457604051631d1f75fb60e31b815260040160405180910390fd5b6040518060400160405280836001600160401b03168152602001826001600160401b031681525061208184611efd565b815160019190910180546020909301516001600160401b03908116600160401b026001600160801b0319909416921691909117919091179055505050565b6120c98282610c56565b6120fc5760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b5f5f5160206131965f395f51905f526121198484610c56565b15611a7a575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506104f1565b5f610cb5836001600160a01b0384166124c0565b5f61219782611efd565b546001600160a01b031692915050565b5f5160206131b65f395f51905f525460ff1661114757604051638dfc202b60e01b815260040160405180910390fd5b6121de6125ce565b6121e782611efd565b6040805160808101825282546001600160a01b031681528151808301835260018401546001600160401b038082168352600160401b918290048116602080850191909152808501939093528451606080820187526002880154808416835293840490921681850152600160801b90920460ff161515828601528385019190915283519182019093526003840180549294938501928290829061228890612b50565b80601f01602080910402602001604051908101604052809291908181526020018280546122b490612b50565b80156122ff5780601f106122d6576101008083540402835291602001916122ff565b820191905f5260205f20905b8154815290600101906020018083116122e257829003601f168201915b5050509190925250505090525092915050565b62ffffff165f9081527f000000000000000000000000000000000000000000000000000000000000000060209081526040918290208251808401909352546001600160401b038116808452600160401b90910460ff1615159290910182905291565b5f825f018281548110612389576123896130ff565b905f5260205f200154905092915050565b6060815f018054806020026020016040519081016040528092919081815260200182805480156123e757602002820191905f5260205f20905b8154815260200190600101908083116123d3575b50505050509050919050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612450573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c249190613113565b5f8181526001830160205260408120546124b957508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556104f1565b505f6104f1565b5f8181526001830160205260408120548015611a7a575f6124e260018361312e565b85549091505f906124f59060019061312e565b9050808214612554575f865f018281548110612513576125136130ff565b905f5260205f200154905080875f018481548110612533576125336130ff565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061256557612565613141565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506104f1565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180608001604052805f6001600160a01b0316815260200161261560405180604001604052805f6001600160401b031681526020015f6001600160401b031681525090565b8152604080516060810182525f808252602082810182905292820152910190815260200161264f6040518060200160405280606081525090565b905290565b5f60208284031215612664575f5ffd5b81356001600160e01b031981168114610cb5575f5ffd5b6001600160a01b0381168114610a75575f5ffd5b6001600160401b0381168114610a75575f5ffd5b5f5f5f5f5f60a086880312156126b7575f5ffd5b85356126c28161267b565b945060208601356126d28161268f565b935060408601356126e28161268f565b925060608601356126f28161268f565b915060808601356127028161268f565b809150509295509295909350565b5f60208284031215612720575f5ffd5b5035919050565b62ffffff81168114610a75575f5ffd5b5f5f5f5f5f5f5f60c0888a03121561274d575f5ffd5b873561275881612727565b965060208801356127688161268f565b955060408801356127788161267b565b945060608801356127888161268f565b935060808801356127988161268f565b925060a08801356001600160401b038111156127b2575f5ffd5b8801601f81018a136127c2575f5ffd5b80356001600160401b038111156127d7575f5ffd5b8a60208284010111156127e8575f5ffd5b602082019350809250505092959891949750929550565b5f5f60408385031215612810575f5ffd5b8235915060208301356128228161267b565b809150509250929050565b5f5f6040838503121561283e575f5ffd5b823561284981612727565b915060208301356128228161268f565b5f60208284031215612869575f5ffd5b8135610cb58161267b565b5f81516020845280518060208601528060208301604087015e5f604082870101526040601f19601f8301168601019250505092915050565b62ffffff851681526001600160401b03841660208201528215156040820152608060608201526001600160a01b0382511660808201525f60208301516001600160401b0381511660a08401526001600160401b0360208201511660c08401525060408301516001600160401b0381511660e08401526001600160401b036020820151166101008401526040810151151561012084015250606083015160e061014084015261295e610160840182612874565b979650505050505050565b5f5f5f5f6080858703121561297c575f5ffd5b84356129878161268f565b935060208501356129978161268f565b925060408501356129a78161268f565b915060608501356129b78161268f565b939692955090935050565b5f5f5f606084860312156129d4575f5ffd5b83356129df81612727565b925060208401356129ef8161268f565b915060408401356129ff8161267b565b809150509250925092565b5f5f60408385031215612a1b575f5ffd5b50508035926020909101359150565b602080825282518282018190525f918401906040840190835b81811015612a6a5783516001600160a01b0316835260209384019390920191600101612a43565b509095945050505050565b5f5f60408385031215612a86575f5ffd5b82356128498161268f565b8015158114610a75575f5ffd5b5f5f5f60608486031215612ab0575f5ffd5b8335612abb81612727565b92506020840135612acb81612a91565b915060408401356129ff8161268f565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b0381811683821601908111156104f1576104f1612b09565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680612b6457607f821691505b602082108103612b8257634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156109b357805f5260205f20601f840160051c81016020851015612bad5750805b601f840160051c820191505b81811015610acb575f8155600101612bb9565b81516001600160401b03811115612be557612be5612b3c565b612bf981612bf38454612b50565b84612b88565b6020601f821160018114612c2b575f8315612c145750848201515b5f19600385901b1c1916600184901b178455610acb565b5f84815260208120601f198516915b82811015612c5a5787850151825560209485019460019092019101612c3a565b5084821015612c7757868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6040516101a081016001600160401b0381118282101715612ca957612ca9612b3c565b60405290565b6040516101e081016001600160401b0381118282101715612ca957612ca9612b3c565b805161172081612727565b80516117208161267b565b805161ffff81168114611720575f5ffd5b805160ff81168114611720575f5ffd5b5f82601f830112612d18575f5ffd5b81516001600160401b03811115612d3157612d31612b3c565b604051601f8201601f19908116603f011681016001600160401b0381118282101715612d5f57612d5f612b3c565b604052818152838201602001851015612d76575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80516117208161268f565b5f60208284031215612dad575f5ffd5b81516001600160401b03811115612dc2575f5ffd5b82016101a08185031215612dd4575f5ffd5b612ddc612c86565b612de582612cd2565b8152612df360208301612cdd565b6020820152612e0460408301612ce8565b6040820152612e1560608301612ce8565b6060820152612e2660808301612ce8565b6080820152612e3760a08301612cf9565b60a082015260c08201516001600160401b03811115612e54575f5ffd5b612e6086828501612d09565b60c083015250612e7260e08301612d92565b60e082015261010082810151908201526101208083015190820152612e9a6101408301612ce8565b610140820152612ead6101608301612d92565b610160820152612ec06101808301612d92565b610180820152949350505050565b5f60208284031215612ede575f5ffd5b5051919050565b805161172081612a91565b5f60208284031215612f00575f5ffd5b8151610cb581612a91565b805163ffffffff81168114611720575f5ffd5b5f6101e0828403128015612f30575f5ffd5b50612f39612caf565b612f4283612f0b565b8152612f5060208401612f0b565b6020820152612f6160408401612f0b565b6040820152612f7260608401612f0b565b6060820152612f8360808401612f0b565b6080820152612f9460a08401612f0b565b60a0820152612fa560c08401612f0b565b60c0820152612fb660e08401612cf9565b60e0820152612fc86101008401612f0b565b610100820152612fdb6101208401612f0b565b610120820152612fee6101408401612cdd565b6101408201526130016101608401612cdd565b6101608201526130146101808401612cdd565b6101808201526130276101a08401612cdd565b6101a082015261303a6101c08401612ee5565b6101c08201529392505050565b5f5f5f5f5f5f60c0878903121561305c575f5ffd5b865161306781612a91565b60208801519096506001600160401b03811115613082575f5ffd5b61308e89828a01612d09565b955050604087015161309f8161267b565b60608801519094506130b08161268f565b60808801519093506130c18161268f565b60a08801519092506130d28161268f565b809150509295509295509295565b6001600160401b0382811682821603908111156104f1576104f1612b09565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613123575f5ffd5b8151610cb58161267b565b818103818111156104f1576104f1612b09565b634e487b7160e01b5f52603160045260245ffdfec1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000794daa56950487582951e8db2fdbcbee68c2223c65641d0aa02a3afc64f9a86f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a164736f6c634300081c000a00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000", + "nonce": "0x45", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xbf3baa41f166f615fc2370f47f338b6b5e56d27f70d9fdeb51efdf8cd61aa2be", + "transactionType": "CALL", + "contractName": "OssifiableProxy", + "contractAddress": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "function": "proxy__upgradeTo(address)", + "arguments": [ + "0x0442fB2C8607D04D6Ba496Ea8990dA7bd7Ec25fc" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "gas": "0xbcc3", + "value": "0x0", + "input": "0x3ebdd0eb0000000000000000000000000442fb2c8607d04d6ba496ea8990da7bd7ec25fc", + "nonce": "0x46", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xfa237667dbb4f06775db358bbb699b0170861becbb005617df009b5138c46164", + "transactionType": "CREATE", + "contractName": "CCR", + "contractAddress": "0x82cfae30820b4249acd6315c90a4d0a9fef5a99a", + "function": null, + "arguments": [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "gas": "0x3c4001", + "value": "0x0", + "input": "0x610100604052348015610010575f5ffd5b5060405161385b38038061385b83398101604081905261002f91610245565b6040517f6c69646f2e636363702e73746f726167652e436f6e66696753746f7261676500602082015260ff1990600190603f01604051602081830303815290604052805190602001205f1c610084919061027c565b60405160200161009691815260200190565b60408051601f198184030181529082905280516020918201209290921660805260ff19916001916100fb91017f6c69646f2e636363702e73746f726167652e4f70657261746f7253746174657381526653746f7261676560c81b602082015260270190565b604051602081830303815290604052805190602001205f1c61011d919061027c565b60405160200161012f91815260200190565b60408051601f1981840301815291905280516020909101201660a0526001600160a01b03821661017257604051630f05a38b60e41b815260040160405180910390fd5b6001600160a01b03821660c05260e081905261018c610193565b50506102a1565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156101e35760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146102425780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f5f60408385031215610256575f5ffd5b82516001600160a01b038116811461026c575f5ffd5b6020939093015192949293505050565b8181038181111561029b57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e05161354f61030c5f395f61211801525f818161058601526126fe01525f818161174b015281816117f00152818161184b01528181611a8d015261230101525f818161159e01528181611c4801528181611d060152611f30015261354f5ff3fe608060405234801561000f575f5ffd5b5060043610610213575f3560e01c8063893d004e1161011f578063b70ddca0116100a9578063cede01ca11610079578063cede01ca14610540578063d4eec5a614610553578063d547741f1461055b578063d6dc6ae71461056e578063dbba4b4814610581575f5ffd5b8063b70ddca0146104cc578063ba6bc8d5146104df578063c3f909d4146104f2578063ca15c8731461052d575f5ffd5b8063a217fddf116100ef578063a217fddf1461046b578063a264756914610472578063a3246ad314610485578063a3714e07146104a5578063ad32563d146104b8575f5ffd5b8063893d004e1461042a5780638aa104351461043d5780639010d07c1461044557806391d1485414610458575f5ffd5b8063389ed267116101a05780635865c60c116101705780635865c60c146103aa57806358c5be18146103cd5780635c975abb146103f857806370bc09f61461040f5780638456cb5914610422575f5ffd5b8063389ed2671461033d5780633c074eb3146103645780633dcd5765146103775780633f4ba83a146103a2575f5ffd5b8063248a9ca3116101e6578063248a9ca3146102a15780632de03aa1146102dd5780632de0920c146103045780632f2ff15d1461031757806336568abe1461032a575f5ffd5b806301ffc9a71461021757806313e094531461023f5780631a6357e5146102545780631d8394bb14610267575b5f5ffd5b61022a6102253660046129b5565b6105a8565b60405190151581526020015b60405180910390f35b61025261024d366004612a04565b6105d2565b005b61022a610262366004612a81565b610795565b61027a610275366004612ab8565b6107db565b6040805193151584526001600160401b039283166020850152911690820152606001610236565b6102cf6102af366004612ad3565b5f9081525f5160206135035f395f51905f52602052604090206001015490565b604051908152602001610236565b6102cf7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b610252610312366004612aea565b6107f4565b610252610325366004612bb2565b610a84565b610252610338366004612bb2565b610aba565b6102cf7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b610252610372366004612a81565b610af2565b61038a610385366004612a81565b610b7d565b6040516001600160401b039091168152602001610236565b610252610c8e565b6103bd6103b8366004612bd5565b610cc3565b6040516102369493929190612c28565b6103e06103db366004612a81565b610cf3565b6040516001600160a01b039091168152602001610236565b5f5160206135235f395f51905f525460ff1661022a565b61025261041d366004612ce5565b610d12565b610252610d3c565b610252610438366004612d3e565b610d6e565b61038a610e5c565b6103e0610453366004612d86565b610e93565b61022a610466366004612bb2565b610eb8565b6102cf5f81565b610252610480366004612db3565b610eee565b610498610493366004612ad3565b610f7e565b6040516102369190612de1565b6103bd6104b3366004612a81565b610fae565b6102cf5f5160206134e35f395f51905f5281565b61038a6104da366004612ab8565b610fe0565b6102526104ed366004612e2c565b61102a565b6104fa61113a565b604080516001600160401b0395861681529385166020850152918416918301919091529091166060820152608001610236565b6102cf61053b366004612ad3565b611154565b61038a61054e366004612ab8565b611178565b6102526111b1565b610252610569366004612bb2565b61128c565b61025261057c366004612a81565b6112bc565b6103e07f000000000000000000000000000000000000000000000000000000000000000081565b5f6001600160e01b03198216635a05180f60e01b14806105cc57506105cc82611386565b92915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156106165750825b90505f826001600160401b031660011480156106315750303b155b90508115801561063f575080155b1561065d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561068757845460ff60401b1916600160401b1785555b6001600160a01b038a166106ae576040516319467aa760e11b815260040160405180910390fd5b6106b66113ba565b6106be6113cc565b6106c85f8b6113d4565b506106e05f5160206134e35f395f51905f528b6113d4565b5061070b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d8b6113d4565b506107367f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c78b6113d4565b5061074389898989611416565b831561078957845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b5f61079e6128fb565b6107a981858561147b565b5f6107b4858561148f565b90505f6107c0826114d8565b90506107d181878560a0015161153e565b9695505050505050565b5f5f5f6107e784611593565b9250925092509193909250565b6107fc611609565b6001600160a01b03851661082357604051634434240960e11b815260040160405180910390fd5b61082b6128fb565b61083681898961147b565b80606001516001600160a01b0316336001600160a01b03161461086c576040516349ad9b0960e11b815260040160405180910390fd5b8060a0015161088e57604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b038716604089901b62ffffff60401b16175f6108b0826114d8565b90505f6108bc82611639565b8051909150156108df576040516342ee68b560e01b815260040160405180910390fd5b8060400151610901576040516328a5715760e21b815260040160405180910390fd5b61090b838a611739565b6040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc9060200160405180910390a3610991836040518060600160405280436001600160401b031681526020015f6001600160401b031681526020015f1515815250611877565b61099d84848a8a6118d8565b604080516020601f8801819004810282018301835281018781526109e09286929182918b908b90819085018382808284375f920191909152505050915250611952565b896001600160401b03168b62ffffff167f8ae0affaa3c6c49aee34fd262b42a1ed56bf92a700857bddf013a00a99bff4ef8888604051610a21929190612e48565b60405180910390a36040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907f742aa8302eca1011d41495047d2d195954b0f91a24ac18e221a1788507e42d7d9060200160405180910390a35050505050505050505050565b5f8281525f5160206135035f395f51905f526020526040902060010154610aaa81611971565b610ab483836113d4565b50505050565b6001600160a01b0381163314610ae35760405163334bd91960e11b815260040160405180910390fd5b610aed828261197b565b505050565b5f5160206134e35f395f51905f52610b0981611971565b5f610b14848461148f565b90505f610b20826114d8565b9050806040015115610b3c575f6040820152610b3c8282611877565b6040516001600160401b0385169062ffffff8716907f2b140fd22a9c33bfd560c6a50d8f4f235e6d85feeb1629b97b62b488f3c95004905f90a35050505050565b5f5f610b8884610fe0565b9050806001600160401b03165f03610ba3575f9150506105cc565b610bab6128fb565b610bb681868661147b565b8060a00151610bc9575f925050506105cc565b5f610bd4868661148f565b90505f610be0826119b4565b6001600160a01b031603610bf9575f93505050506105cc565b5f610c03826119ce565b90505f815f01518260200151610c199190612e8a565b610c24906001612ea9565b9050846001600160401b0316816001600160401b031610610c4c575f955050505050506105cc565b80850395505f818560800151610c629190612e8a565b9050866001600160401b0316816001600160401b03161015610c82578096505b50505050505092915050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7610cb881611971565b610cc0611a1c565b50565b5f5f5f610cce61292f565b5f610cd886611a7b565b9050610ce381611adb565b9450945094509450509193509193565b5f5f610cff848461148f565b9050610d0a816119b4565b949350505050565b5f5160206134e35f395f51905f52610d2981611971565b610d3585858585611416565b5050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d610d6681611971565b610cc0611b2b565b610d76611609565b6001600160a01b038116610d9d57604051634434240960e11b815260040160405180910390fd5b610da56128fb565b610db081858561147b565b80606001516001600160a01b0316336001600160a01b031614610de6576040516349ad9b0960e11b815260040160405180910390fd5b610e0762ffffff60401b604086901b166001600160401b0385161783611739565b6040516001600160a01b03831681526001600160401b0384169062ffffff8616907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc906020015b60405180910390a350505050565b5f610e8e7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00546001600160401b031690565b905090565b5f8281525f5160206134c35f395f51905f52602081905260408220610d0a9084611b73565b5f9182525f5160206135035f395f51905f52602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f5160206134e35f395f51905f52610f0581611971565b610f0d6128fb565b610f178187611b7e565b610f2386868686611c14565b6040805186151581526001600160401b038681166020830152851681830152905162ffffff8816917f37874944470fa7fc10b8f30fa6686dc6b2f96522f58cddd220c8a5856400652b919081900360600190a2505050505050565b5f8181525f5160206134c35f395f51905f526020819052604090912060609190610fa790611cec565b9392505050565b5f5f5f610fb961292f565b5f610fc4878761148f565b9050610fcf81611adb565b929a91995097509095509350505050565b5f5f610fea611cf8565b50925050505f5f610ffa85611593565b50915091508161101f576001600160401b038116156110195780611021565b82611021565b5f5b95945050505050565b611032611609565b5f61103c33611a7b565b90505f61105061104b836114d8565b611639565b805190915061107257604051631a8660cb60e01b815260040160405180910390fd5b5f61107c836119ce565b9050805f01516001600160401b0316856001600160401b031611806110b6575080602001516001600160401b0316846001600160401b0316105b806110ed575080516001600160401b0386811691161480156110ed575080602001516001600160401b0316846001600160401b0316145b1561110b5760405163c7f544bb60e01b815260040160405180910390fd5b6111136128fb565b604084901c8461112483838361147b565b61113083878a8a6118d8565b5050505050505050565b5f5f5f5f611146611cf8565b935093509350935090919293565b5f8181525f5160206134c35f395f51905f52602081905260408220610fa790611d6e565b5f5f611182611cf8565b93505050505f5f61119285611593565b92505091508161101f576001600160401b038116156110195780611021565b6111b9611609565b5f6111c333611a7b565b90505f6111cf826114d8565b90505f6111db82611639565b80519091506111fd57604051631a8660cb60e01b815260040160405180910390fd5b806060015161121f57604051630eedec1160e31b815260040160405180910390fd5b6001600160401b03431660208301526112388383611877565b604080515f81529084901c9084906001600160401b0382169062ffffff8416907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a35050505050565b5f8281525f5160206135035f395f51905f5260205260409020600101546112b281611971565b610ab4838361197b565b5f5160206134e35f395f51905f526112d381611971565b5f6112de848461148f565b90505f6112ea826114d8565b90505f6112f682611639565b805190915061131857604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b0343166020830152600160408301526113388383611877565b604051600181526001600160401b0386169062ffffff8816907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a3505050505050565b5f6001600160e01b03198216637965db0b60e01b14806105cc57506301ffc9a760e01b6001600160e01b03198316146105cc565b6113c2611d77565b6113ca611dc0565b565b6113ca611d77565b5f5f5160206134c35f395f51905f52816113ee8585611de0565b90508015610d0a575f85815260208390526040902061140d9085611e81565b50949350505050565b61142284848484611e95565b604080516001600160401b0386811682528581166020830152848116828401528316606082015290517f84d25455db084cf777a0a4a00fcc28946b3a2ebc5cc866ef8820ab8571dbdd369181900360800190a150505050565b6114858383611b7e565b610aed8382611fd2565b6001600160401b038116604083901b62ffffff60401b16175f6114b1826119b4565b6001600160a01b0316036105cc576040516325ec6c1f60e01b815260040160405180910390fd5b604080516060810182525f80825260208201819052918101919091526114fd826122fb565b60408051606081018252600292909201546001600160401b038082168452600160401b8204166020840152600160801b900460ff1615159082015292915050565b5f5f61154985611639565b90505f61155585611593565b505082519091508015611566575080155b801561156f5750835b80156107d157505f5160206135235f395f51905f525460ff16159695505050505050565b62ffffff165f9081527f000000000000000000000000000000000000000000000000000000000000000060209081526040918290208251606081018452905460ff811615158083526001600160401b0361010083048116948401859052600160481b909204909116919093018190529192909190565b5f5160206135235f395f51905f525460ff16156113ca5760405163d93c066560e01b815260040160405180910390fd5b604080516080810182525f808252602082018190529181018290526060810191909152435f80611667611cf8565b5050915091505f5f86602001516001600160401b03161180156116aa5750836001600160401b031682876020015161169f9190612ea9565b6001600160401b0316105b90505f5f875f01516001600160401b03161180156116c6575081155b90505f811580156116d957508760400151155b90505f828015611707575088516001600160401b038816906116fc908890612ea9565b6001600160401b0316105b604080516080810182529415158552941515602085015291151593830193909352151560608201529695505050505050565b6001600160a01b0381165f90815260017f000000000000000000000000000000000000000000000000000000000000000001602052604090205480158015906117825750828114155b156117a05760405163324e8e5f60e01b815260040160405180910390fd5b5f6117aa846122fb565b80549091506001600160a01b031680158015906117d95750836001600160a01b0316816001600160a01b031614155b15611819576001600160a01b0381165f90815260017f00000000000000000000000000000000000000000000000000000000000000000160205260408120555b50805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03939093169283179055505f9081527f00000000000000000000000000000000000000000000000000000000000000006001016020526040902055565b80611881836122fb565b8151600291909101805460208401516040909401511515600160801b0260ff60801b196001600160401b03958616600160401b026001600160801b0319909316959094169490941717919091169190911790555050565b6118e78460800151838361232a565b83516118f49083836123ae565b6118ff83838361242e565b604080850151855182516001600160401b03868116825285811660208301529092169262ffffff909116917f9841e87ed1a3c39f013566d8bc2c2119007cfb8c591ccde84c6e012e8891410c9101610e4e565b8061195c836122fb565b8151600391909101908190610d359082612f58565b610cc0813361249c565b5f5f5160206134c35f395f51905f528161199585856124dd565b90508015610d0a575f85815260208390526040902061140d9085612556565b5f6119be826122fb565b546001600160a01b031692915050565b604080518082019091525f80825260208201526119ea826122fb565b60408051808201909152600191909101546001600160401b038082168352600160401b90910416602082015292915050565b611a2461256a565b5f5160206135235f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b6001600160a01b0381165f90815260017f000000000000000000000000000000000000000000000000000000000000000001602052604081205490819003611ad65760405163fe0a2bb160e01b815260040160405180910390fd5b919050565b5f5f5f611ae661292f565b611aee6128fb565b604086901c9450859350611b0381868661147b565b611b0c86612599565b9150611b218260400151868360a0015161153e565b9250509193509193565b611b33611609565b5f5160206135235f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833611a5d565b5f610fa783836126d5565b5f611b876126fb565b604051630bc1bb1960e41b815262ffffff841660048201526001600160a01b03919091169063bc1bb190906024015f60405180830381865afa158015611bcf573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611bf69190810190613129565b62ffffff9092168352506020908101516001600160a01b0316910152565b60405180606001604052808415158152602001836001600160401b03168152602001826001600160401b0316815250611c6a7f000000000000000000000000000000000000000000000000000000000000000090565b62ffffff959095165f90815260209586526040908190208251815497840151939092015168ffffffffffffffffff1990971691151568ffffffffffffffff001916919091176101006001600160401b03938416021770ffffffffffffffff0000000000000000001916600160481b929096169190910294909417909355505050565b60605f610fa78361277c565b6040805160808101825260017f000000000000000000000000000000000000000000000000000000000000000001546001600160401b03808216808452600160401b8304821660208501819052600160801b84048316958501869052600160c01b90930490911660609093018390529390929190565b5f6105cc825490565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166113ca57604051631afcd79f60e31b815260040160405180910390fd5b611dc8611d77565b5f5160206135235f395f51905f52805460ff19169055565b5f5f5160206135035f395f51905f52611df98484610eb8565b611e78575f848152602082815260408083206001600160a01b03871684529091529020805460ff19166001179055611e2e3390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506105cc565b5f9150506105cc565b5f610fa7836001600160a01b0384166127d5565b816001600160401b03165f03611ebe5760405163ad617a0b60e01b815260040160405180910390fd5b806001600160401b03165f03611ee657604051628faa1f60e61b815260040160405180910390fd5b6040518060800160405280856001600160401b03168152602001846001600160401b03168152602001836001600160401b03168152602001826001600160401b0316815250611f527f000000000000000000000000000000000000000000000000000000000000000090565b81516001919091018054602084015160408501516060909501516001600160401b03908116600160c01b026001600160c01b03968216600160801b02969096166fffffffffffffffffffffffffffffffff928216600160401b026001600160801b0319909416919095161791909117169190911791909117905550505050565b815162ffffff165f03611ff857604051634632571560e01b815260040160405180910390fd5b5f82602001516001600160a01b031663a70c70e46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612039573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061205d919061325a565b9050806001600160401b0316826001600160401b0316106120915760405163b9aa612760e01b815260040160405180910390fd5b8183604001906001600160401b031690816001600160401b0316815250505f83602001516001600160a01b03166315dae03e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120f0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612114919061325a565b90507f00000000000000000000000000000000000000000000000000000000000000008103612252576020840151604051630bc5f72160e31b81526001600160401b03851660048201526001600160a01b03821690635e2fb90890602401602060405180830381865afa15801561218d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121b1919061327c565b151560a08601526040516365c14dc760e01b81526001600160401b03851660048201525f906001600160a01b038316906365c14dc7906024016101e060405180830381865afa158015612206573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061222a91906132aa565b6101808101516001600160a01b031660608801525163ffffffff16608087015250610ab49050565b6020840151604051632695a60f60e21b81526001600160401b03851660048201525f60248201526001600160a01b03821690639a56983c906044015f60405180830381865afa1580156122a7573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526122ce91908101906133d3565b6001600160401b031660808b015250506001600160a01b0316606088015250151560a08601525050505050565b5f9081527f00000000000000000000000000000000000000000000000000000000000000006020526040902090565b806001600160401b0316826001600160401b0316111561235d5760405163c7f544bb60e01b815260040160405180910390fd5b826001600160401b0316816001600160401b03161015806123905750826001600160401b0316826001600160401b031610155b15610aed57604051637ba485c560e01b815260040160405180910390fd5b5f6123b884610fe0565b9050806001600160401b03165f036123e357604051630dbfe5fd60e31b815260040160405180910390fd5b5f6123ee8484612e8a565b6123f9906001612ea9565b9050816001600160401b0316816001600160401b03161115610d3557604051631d1f75fb60e31b815260040160405180910390fd5b6040518060400160405280836001600160401b03168152602001826001600160401b031681525061245e846122fb565b815160019190910180546020909301516001600160401b03908116600160401b026001600160801b0319909416921691909117919091179055505050565b6124a68282610eb8565b6124d95760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b5f5f5160206135035f395f51905f526124f68484610eb8565b15611e78575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506105cc565b5f610fa7836001600160a01b038416612821565b5f5160206135235f395f51905f525460ff166113ca57604051638dfc202b60e01b815260040160405180910390fd5b6125a161292f565b6125aa826122fb565b6040805160808101825282546001600160a01b031681528151808301835260018401546001600160401b038082168352600160401b918290048116602080850191909152808501939093528451606080820187526002880154808416835293840490921681850152600160801b90920460ff161515828601528385019190915283519182019093526003840180549294938501928290829061264b90612edc565b80601f016020809104026020016040519081016040528092919081815260200182805461267790612edc565b80156126c25780601f10612699576101008083540402835291602001916126c2565b820191905f5260205f20905b8154815290600101906020018083116126a557829003601f168201915b5050509190925250505090525092915050565b5f825f0182815481106126ea576126ea61346c565b905f5260205f200154905092915050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612758573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e8e9190613480565b6060815f018054806020026020016040519081016040528092919081815260200182805480156127c957602002820191905f5260205f20905b8154815260200190600101908083116127b5575b50505050509050919050565b5f81815260018301602052604081205461281a57508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556105cc565b505f6105cc565b5f8181526001830160205260408120548015611e78575f61284360018361349b565b85549091505f906128569060019061349b565b90508082146128b5575f865f0182815481106128745761287461346c565b905f5260205f200154905080875f0184815481106128945761289461346c565b5f918252602080832090910192909255918252600188019052604090208390555b85548690806128c6576128c66134ae565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506105cc565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180608001604052805f6001600160a01b0316815260200161297660405180604001604052805f6001600160401b031681526020015f6001600160401b031681525090565b8152604080516060810182525f80825260208281018290529282015291019081526020016129b06040518060200160405280606081525090565b905290565b5f602082840312156129c5575f5ffd5b81356001600160e01b031981168114610fa7575f5ffd5b6001600160a01b0381168114610cc0575f5ffd5b6001600160401b0381168114610cc0575f5ffd5b5f5f5f5f5f60a08688031215612a18575f5ffd5b8535612a23816129dc565b94506020860135612a33816129f0565b93506040860135612a43816129f0565b92506060860135612a53816129f0565b91506080860135612a63816129f0565b809150509295509295909350565b62ffffff81168114610cc0575f5ffd5b5f5f60408385031215612a92575f5ffd5b8235612a9d81612a71565b91506020830135612aad816129f0565b809150509250929050565b5f60208284031215612ac8575f5ffd5b8135610fa781612a71565b5f60208284031215612ae3575f5ffd5b5035919050565b5f5f5f5f5f5f5f60c0888a031215612b00575f5ffd5b8735612b0b81612a71565b96506020880135612b1b816129f0565b95506040880135612b2b816129dc565b94506060880135612b3b816129f0565b93506080880135612b4b816129f0565b925060a08801356001600160401b03811115612b65575f5ffd5b8801601f81018a13612b75575f5ffd5b80356001600160401b03811115612b8a575f5ffd5b8a6020828401011115612b9b575f5ffd5b602082019350809250505092959891949750929550565b5f5f60408385031215612bc3575f5ffd5b823591506020830135612aad816129dc565b5f60208284031215612be5575f5ffd5b8135610fa7816129dc565b5f81516020845280518060208601528060208301604087015e5f604082870101526040601f19601f8301168601019250505092915050565b62ffffff851681526001600160401b03841660208201528215156040820152608060608201526001600160a01b0382511660808201525f60208301516001600160401b0381511660a08401526001600160401b0360208201511660c08401525060408301516001600160401b0381511660e08401526001600160401b036020820151166101008401526040810151151561012084015250606083015160e0610140840152612cda610160840182612bf0565b979650505050505050565b5f5f5f5f60808587031215612cf8575f5ffd5b8435612d03816129f0565b93506020850135612d13816129f0565b92506040850135612d23816129f0565b91506060850135612d33816129f0565b939692955090935050565b5f5f5f60608486031215612d50575f5ffd5b8335612d5b81612a71565b92506020840135612d6b816129f0565b91506040840135612d7b816129dc565b809150509250925092565b5f5f60408385031215612d97575f5ffd5b50508035926020909101359150565b8015158114610cc0575f5ffd5b5f5f5f5f60808587031215612dc6575f5ffd5b8435612dd181612a71565b93506020850135612d1381612da6565b602080825282518282018190525f918401906040840190835b81811015612e215783516001600160a01b0316835260209384019390920191600101612dfa565b509095945050505050565b5f5f60408385031215612e3d575f5ffd5b8235612a9d816129f0565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b0382811682821603908111156105cc576105cc612e76565b6001600160401b0381811683821601908111156105cc576105cc612e76565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680612ef057607f821691505b602082108103612f0e57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115610aed57805f5260205f20601f840160051c81016020851015612f395750805b601f840160051c820191505b81811015610d35575f8155600101612f45565b81516001600160401b03811115612f7157612f71612ec8565b612f8581612f7f8454612edc565b84612f14565b6020601f821160018114612fb7575f8315612fa05750848201515b5f19600385901b1c1916600184901b178455610d35565b5f84815260208120601f198516915b82811015612fe65787850151825560209485019460019092019101612fc6565b508482101561300357868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6040516101a081016001600160401b038111828210171561303557613035612ec8565b60405290565b6040516101e081016001600160401b038111828210171561303557613035612ec8565b8051611ad681612a71565b8051611ad6816129dc565b805161ffff81168114611ad6575f5ffd5b805160ff81168114611ad6575f5ffd5b5f82601f8301126130a4575f5ffd5b81516001600160401b038111156130bd576130bd612ec8565b604051601f8201601f19908116603f011681016001600160401b03811182821017156130eb576130eb612ec8565b604052818152838201602001851015613102575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b8051611ad6816129f0565b5f60208284031215613139575f5ffd5b81516001600160401b0381111561314e575f5ffd5b82016101a08185031215613160575f5ffd5b613168613012565b6131718261305e565b815261317f60208301613069565b602082015261319060408301613074565b60408201526131a160608301613074565b60608201526131b260808301613074565b60808201526131c360a08301613085565b60a082015260c08201516001600160401b038111156131e0575f5ffd5b6131ec86828501613095565b60c0830152506131fe60e0830161311e565b60e0820152610100828101519082015261012080830151908201526132266101408301613074565b610140820152613239610160830161311e565b61016082015261324c610180830161311e565b610180820152949350505050565b5f6020828403121561326a575f5ffd5b5051919050565b8051611ad681612da6565b5f6020828403121561328c575f5ffd5b8151610fa781612da6565b805163ffffffff81168114611ad6575f5ffd5b5f6101e08284031280156132bc575f5ffd5b506132c561303b565b6132ce83613297565b81526132dc60208401613297565b60208201526132ed60408401613297565b60408201526132fe60608401613297565b606082015261330f60808401613297565b608082015261332060a08401613297565b60a082015261333160c08401613297565b60c082015261334260e08401613085565b60e08201526133546101008401613297565b6101008201526133676101208401613297565b61012082015261337a6101408401613069565b61014082015261338d6101608401613069565b6101608201526133a06101808401613069565b6101808201526133b36101a08401613069565b6101a08201526133c66101c08401613271565b6101c08201529392505050565b5f5f5f5f5f5f60c087890312156133e8575f5ffd5b86516133f381612da6565b60208801519096506001600160401b0381111561340e575f5ffd5b61341a89828a01613095565b955050604087015161342b816129dc565b606088015190945061343c816129f0565b608088015190935061344d816129f0565b60a088015190925061345e816129f0565b809150509295509295509295565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613490575f5ffd5b8151610fa7816129dc565b818103818111156105cc576105cc612e76565b634e487b7160e01b5f52603160045260245ffdfec1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000794daa56950487582951e8db2fdbcbee68c2223c65641d0aa02a3afc64f9a86f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a164736f6c634300081c000a00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000", + "nonce": "0x47", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x867f5b6a3545347409a33493a0b80ba7e75ed96b54d7b7b56eff08ea9609a54f", + "transactionType": "CALL", + "contractName": "OssifiableProxy", + "contractAddress": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "function": "proxy__upgradeTo(address)", + "arguments": [ + "0x82CFAE30820b4249acD6315C90A4D0a9fEF5A99A" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "gas": "0xbcc3", + "value": "0x0", + "input": "0x3ebdd0eb00000000000000000000000082cfae30820b4249acd6315c90a4d0a9fef5a99a", + "nonce": "0x48", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x7bec86c8525d9afe2a2ae2cce7526d114800521780f991aad20a53b06e363dee", + "transactionType": "CREATE", + "contractName": "CCR", + "contractAddress": "0x76773e5a432113238be489f9a7bf716e8dc4a015", + "function": null, + "arguments": [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "gas": "0x3d360c", + "value": "0x0", + "input": "0x610100604052348015610010575f5ffd5b5060405161393b38038061393b83398101604081905261002f91610245565b6040517f6c69646f2e636363702e73746f726167652e436f6e66696753746f7261676500602082015260ff1990600190603f01604051602081830303815290604052805190602001205f1c610084919061027c565b60405160200161009691815260200190565b60408051601f198184030181529082905280516020918201209290921660805260ff19916001916100fb91017f6c69646f2e636363702e73746f726167652e4f70657261746f7253746174657381526653746f7261676560c81b602082015260270190565b604051602081830303815290604052805190602001205f1c61011d919061027c565b60405160200161012f91815260200190565b60408051601f1981840301815291905280516020909101201660a0526001600160a01b03821661017257604051630f05a38b60e41b815260040160405180910390fd5b6001600160a01b03821660c05260e081905261018c610193565b50506102a1565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156101e35760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146102425780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f5f60408385031215610256575f5ffd5b82516001600160a01b038116811461026c575f5ffd5b6020939093015192949293505050565b8181038181111561029b57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e05161362f61030c5f395f6121f801525f818161058601526127de01525f81816117d90152818161187e015281816118d901528181611b5301526123e101525f818161160d01528181611d2801528181611de60152612010015261362f5ff3fe608060405234801561000f575f5ffd5b5060043610610213575f3560e01c8063893d004e1161011f578063b70ddca0116100a9578063cede01ca11610079578063cede01ca14610540578063d4eec5a614610553578063d547741f1461055b578063d6dc6ae71461056e578063dbba4b4814610581575f5ffd5b8063b70ddca0146104cc578063ba6bc8d5146104df578063c3f909d4146104f2578063ca15c8731461052d575f5ffd5b8063a217fddf116100ef578063a217fddf1461046b578063a264756914610472578063a3246ad314610485578063a3714e07146104a5578063ad32563d146104b8575f5ffd5b8063893d004e1461042a5780638aa104351461043d5780639010d07c1461044557806391d1485414610458575f5ffd5b8063389ed267116101a05780635865c60c116101705780635865c60c146103aa57806358c5be18146103cd5780635c975abb146103f857806370bc09f61461040f5780638456cb5914610422575f5ffd5b8063389ed2671461033d5780633c074eb3146103645780633dcd5765146103775780633f4ba83a146103a2575f5ffd5b8063248a9ca3116101e6578063248a9ca3146102a15780632de03aa1146102dd5780632de0920c146103045780632f2ff15d1461031757806336568abe1461032a575f5ffd5b806301ffc9a71461021757806313e094531461023f5780631a6357e5146102545780631d8394bb14610267575b5f5ffd5b61022a610225366004612a95565b6105a8565b60405190151581526020015b60405180910390f35b61025261024d366004612ae4565b6105d2565b005b61022a610262366004612b61565b610795565b61027a610275366004612b98565b6107e6565b6040805193151584526001600160401b039283166020850152911690820152606001610236565b6102cf6102af366004612bb3565b5f9081525f5160206135e35f395f51905f52602052604090206001015490565b604051908152602001610236565b6102cf7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b610252610312366004612bca565b6107ff565b610252610325366004612c92565b610a8f565b610252610338366004612c92565b610ac5565b6102cf7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b610252610372366004612b61565b610afd565b61038a610385366004612b61565b610b88565b6040516001600160401b039091168152602001610236565b610252610cb2565b6103bd6103b8366004612cb5565b610ce7565b6040516102369493929190612d08565b6103e06103db366004612b61565b610d17565b6040516001600160a01b039091168152602001610236565b5f5160206136035f395f51905f525460ff1661022a565b61025261041d366004612dc5565b610d36565b610252610d60565b610252610438366004612e1e565b610d92565b61038a610ed3565b6103e0610453366004612e66565b610f0a565b61022a610466366004612c92565b610f2f565b6102cf5f81565b610252610480366004612e93565b610f65565b610498610493366004612bb3565b610ff5565b6040516102369190612ec1565b6103bd6104b3366004612b61565b611025565b6102cf5f5160206135c35f395f51905f5281565b61038a6104da366004612b98565b611057565b6102526104ed366004612f0c565b6110a1565b6104fa6111cf565b604080516001600160401b0395861681529385166020850152918416918301919091529091166060820152608001610236565b6102cf61053b366004612bb3565b6111e9565b61038a61054e366004612b98565b61120d565b610252611246565b610252610569366004612c92565b611321565b61025261057c366004612b61565b611351565b6103e07f000000000000000000000000000000000000000000000000000000000000000081565b5f6001600160e01b03198216635a05180f60e01b14806105cc57506105cc8261143e565b92915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156106165750825b90505f826001600160401b031660011480156106315750303b155b90508115801561063f575080155b1561065d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561068757845460ff60401b1916600160401b1785555b6001600160a01b038a166106ae576040516319467aa760e11b815260040160405180910390fd5b6106b6611472565b6106be611484565b6106c85f8b61148c565b506106e05f5160206135c35f395f51905f528b61148c565b5061070b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d8b61148c565b506107367f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c78b61148c565b50610743898989896114ce565b831561078957845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b5f61079e6129db565b6107a9818585611533565b6001600160401b038316604085901b62ffffff60401b16175f6107cb82611547565b90506107dc81878560a001516115ad565b9695505050505050565b5f5f5f6107f284611602565b9250925092509193909250565b610807611678565b6001600160a01b03851661082e57604051634434240960e11b815260040160405180910390fd5b6108366129db565b610841818989611533565b80606001516001600160a01b0316336001600160a01b031614610877576040516349ad9b0960e11b815260040160405180910390fd5b8060a0015161089957604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b038716604089901b62ffffff60401b16175f6108bb82611547565b90505f6108c7826116a8565b8051909150156108ea576040516348a1843960e01b815260040160405180910390fd5b806040015161090c576040516328a5715760e21b815260040160405180910390fd5b610916838a6117c7565b6040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc9060200160405180910390a361099c836040518060600160405280436001600160401b031681526020015f6001600160401b031681526020015f1515815250611905565b6109a884848a8a611966565b604080516020601f8801819004810282018301835281018781526109eb9286929182918b908b90819085018382808284375f9201919091525050509152506119e9565b896001600160401b03168b62ffffff167f8ae0affaa3c6c49aee34fd262b42a1ed56bf92a700857bddf013a00a99bff4ef8888604051610a2c929190612f28565b60405180910390a36040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907f742aa8302eca1011d41495047d2d195954b0f91a24ac18e221a1788507e42d7d9060200160405180910390a35050505050505050505050565b5f8281525f5160206135e35f395f51905f526020526040902060010154610ab581611a08565b610abf838361148c565b50505050565b6001600160a01b0381163314610aee5760405163334bd91960e11b815260040160405180910390fd5b610af88282611a12565b505050565b5f5160206135c35f395f51905f52610b1481611a08565b5f610b1f8484611a4b565b90505f610b2b82611547565b9050806040015115610b47575f6040820152610b478282611905565b6040516001600160401b0385169062ffffff8716907f2b140fd22a9c33bfd560c6a50d8f4f235e6d85feeb1629b97b62b488f3c95004905f90a35050505050565b5f610b9283611057565b9050806001600160401b03165f03610bab57505f6105cc565b610bb36129db565b610bbe818585611533565b8060a00151610bd0575f9150506105cc565b80608001516001600160401b0316826001600160401b03161115610bf657806080015191505b6001600160401b038316604085901b62ffffff60401b16175f610c20610c1b83611547565b6116a8565b80519091508015610c3357508060200151155b15610c95575f610c4283611a94565b90505f815f01518260200151610c589190612f6a565b610c63906001612f89565b9050856001600160401b0316816001600160401b031610610c8b575f955050505050506105cc565b9094039350610ca9565b8060400151610ca9575f93505050506105cc565b50505092915050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7610cdc81611a08565b610ce4611ae2565b50565b5f5f5f610cf2612a0f565b5f610cfc86611b41565b9050610d0781611ba1565b9450945094509450509193509193565b5f5f610d238484611a4b565b9050610d2e81611bf1565b949350505050565b5f5160206135c35f395f51905f52610d4d81611a08565b610d59858585856114ce565b5050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d610d8a81611a08565b610ce4611c0b565b610d9a611678565b6001600160a01b038116610dc157604051634434240960e11b815260040160405180910390fd5b610dc96129db565b610dd4818585611533565b80606001516001600160a01b0316336001600160a01b031614610e0a576040516349ad9b0960e11b815260040160405180910390fd5b5f610e158585611a4b565b90505f610e2182611547565b90505f610e2d826116a8565b8051909150610e4f57604051632acf1f2560e11b815260040160405180910390fd5b806020015115610e7257604051630a93f65160e11b815260040160405180910390fd5b610e7c83866117c7565b6040516001600160a01b03861681526001600160401b0387169062ffffff8916907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc9060200160405180910390a350505050505050565b5f610f057ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00546001600160401b031690565b905090565b5f8281525f5160206135a35f395f51905f52602081905260408220610d2e9084611c53565b5f9182525f5160206135e35f395f51905f52602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f5160206135c35f395f51905f52610f7c81611a08565b610f846129db565b610f8e8187611c5e565b610f9a86868686611cf4565b6040805186151581526001600160401b038681166020830152851681830152905162ffffff8816917f37874944470fa7fc10b8f30fa6686dc6b2f96522f58cddd220c8a5856400652b919081900360600190a2505050505050565b5f8181525f5160206135a35f395f51905f52602081905260409091206060919061101e90611dcc565b9392505050565b5f5f5f611030612a0f565b5f61103b8787611a4b565b905061104681611ba1565b929a91995097509095509350505050565b5f5f611061611dd8565b50925050505f5f61107185611602565b509150915081611096576001600160401b038116156110905780611098565b82611098565b5f5b95945050505050565b6110a9611678565b5f6110b333611b41565b90505f6110c2610c1b83611547565b80519091506110e457604051632acf1f2560e11b815260040160405180910390fd5b80602001511561110757604051630a93f65160e11b815260040160405180910390fd5b5f61111183611a94565b9050805f01516001600160401b0316856001600160401b0316118061114b575080602001516001600160401b0316846001600160401b0316105b80611182575080516001600160401b038681169116148015611182575080602001516001600160401b0316846001600160401b0316145b156111a05760405163c7f544bb60e01b815260040160405180910390fd5b6111a86129db565b604084901c846111b9838383611533565b6111c583878a8a611966565b5050505050505050565b5f5f5f5f6111db611dd8565b935093509350935090919293565b5f8181525f5160206135a35f395f51905f5260208190526040822061101e90611e4e565b5f5f611217611dd8565b93505050505f5f61122785611602565b925050915081611096576001600160401b038116156110905780611098565b61124e611678565b5f61125833611b41565b90505f61126482611547565b90505f611270826116a8565b805190915061129257604051632acf1f2560e11b815260040160405180910390fd5b80606001516112b457604051630eedec1160e31b815260040160405180910390fd5b6001600160401b03431660208301526112cd8383611905565b604080515f81529084901c9084906001600160401b0382169062ffffff8416907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a35050505050565b5f8281525f5160206135e35f395f51905f52602052604090206001015461134781611a08565b610abf8383611a12565b5f5160206135c35f395f51905f5261136881611a08565b5f6113738484611a4b565b90505f61137f82611547565b90505f61138b826116a8565b80519091506113ad57604051632acf1f2560e11b815260040160405180910390fd5b8060200151156113d057604051630a93f65160e11b815260040160405180910390fd5b6001600160401b0343166020830152600160408301526113f08383611905565b604051600181526001600160401b0386169062ffffff8816907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a3505050505050565b5f6001600160e01b03198216637965db0b60e01b14806105cc57506301ffc9a760e01b6001600160e01b03198316146105cc565b61147a611e57565b611482611ea0565b565b611482611e57565b5f5f5160206135a35f395f51905f52816114a68585611ec0565b90508015610d2e575f8581526020839052604090206114c59085611f61565b50949350505050565b6114da84848484611f75565b604080516001600160401b0386811682528581166020830152848116828401528316606082015290517f84d25455db084cf777a0a4a00fcc28946b3a2ebc5cc866ef8820ab8571dbdd369181900360800190a150505050565b61153d8383611c5e565b610af883826120b2565b604080516060810182525f808252602082018190529181019190915261156c826123db565b60408051606081018252600292909201546001600160401b038082168452600160401b8204166020840152600160801b900460ff1615159082015292915050565b5f5f6115b8856116a8565b90505f6115c485611602565b5050825190915080156115d5575080155b80156115de5750835b80156107dc57505f5160206136035f395f51905f525460ff16159695505050505050565b62ffffff165f9081527f000000000000000000000000000000000000000000000000000000000000000060209081526040918290208251606081018452905460ff811615158083526001600160401b0361010083048116948401859052600160481b909204909116919093018190529192909190565b5f5160206136035f395f51905f525460ff16156114825760405163d93c066560e01b815260040160405180910390fd5b604080516080810182525f808252602082018190529181018290526060810191909152435f806116d6611dd8565b5050915091505f5f86602001516001600160401b031611801561171a5750836001600160401b031682876020015161170e9190612f89565b6001600160401b031610155b90505f5f875f01516001600160401b031611801561174a575060208701516001600160401b0316158061174a5750815b90505f8115801561175d57508760400151155b90505f82801561176b575083155b8015611795575088516001600160401b0388169061178a908890612f89565b6001600160401b0316105b604080516080810182529415158552941515602085015291151593830193909352151560608201529695505050505050565b6001600160a01b0381165f90815260017f000000000000000000000000000000000000000000000000000000000000000001602052604090205480158015906118105750828114155b1561182e5760405163324e8e5f60e01b815260040160405180910390fd5b5f611838846123db565b80549091506001600160a01b031680158015906118675750836001600160a01b0316816001600160a01b031614155b156118a7576001600160a01b0381165f90815260017f00000000000000000000000000000000000000000000000000000000000000000160205260408120555b50805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03939093169283179055505f9081527f00000000000000000000000000000000000000000000000000000000000000006001016020526040902055565b8061190f836123db565b8151600291909101805460208401516040909401511515600160801b0260ff60801b196001600160401b03958616600160401b026001600160801b0319909316959094169490941717919091169190911790555050565b6119758460800151838361240a565b835161198290838361248e565b61198d83838361250e565b604080850151855182516001600160401b03868116825285811660208301529092169262ffffff909116917f9841e87ed1a3c39f013566d8bc2c2119007cfb8c591ccde84c6e012e8891410c910160405180910390a350505050565b806119f3836123db565b8151600391909101908190610d599082613038565b610ce4813361257c565b5f5f5160206135a35f395f51905f5281611a2c85856125bd565b90508015610d2e575f8581526020839052604090206114c59085612636565b6001600160401b038116604083901b62ffffff60401b16175f611a6d82611bf1565b6001600160a01b0316036105cc576040516325ec6c1f60e01b815260040160405180910390fd5b604080518082019091525f8082526020820152611ab0826123db565b60408051808201909152600191909101546001600160401b038082168352600160401b90910416602082015292915050565b611aea61264a565b5f5160206136035f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b6001600160a01b0381165f90815260017f000000000000000000000000000000000000000000000000000000000000000001602052604081205490819003611b9c5760405163fe0a2bb160e01b815260040160405180910390fd5b919050565b5f5f5f611bac612a0f565b611bb46129db565b604086901c9450859350611bc9818686611533565b611bd286612679565b9150611be78260400151868360a001516115ad565b9250509193509193565b5f611bfb826123db565b546001600160a01b031692915050565b611c13611678565b5f5160206136035f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833611b23565b5f61101e83836127b5565b5f611c676127db565b604051630bc1bb1960e41b815262ffffff841660048201526001600160a01b03919091169063bc1bb190906024015f60405180830381865afa158015611caf573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611cd69190810190613209565b62ffffff9092168352506020908101516001600160a01b0316910152565b60405180606001604052808415158152602001836001600160401b03168152602001826001600160401b0316815250611d4a7f000000000000000000000000000000000000000000000000000000000000000090565b62ffffff959095165f90815260209586526040908190208251815497840151939092015168ffffffffffffffffff1990971691151568ffffffffffffffff001916919091176101006001600160401b03938416021770ffffffffffffffff0000000000000000001916600160481b929096169190910294909417909355505050565b60605f61101e8361285c565b6040805160808101825260017f000000000000000000000000000000000000000000000000000000000000000001546001600160401b03808216808452600160401b8304821660208501819052600160801b84048316958501869052600160c01b90930490911660609093018390529390929190565b5f6105cc825490565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661148257604051631afcd79f60e31b815260040160405180910390fd5b611ea8611e57565b5f5160206136035f395f51905f52805460ff19169055565b5f5f5160206135e35f395f51905f52611ed98484610f2f565b611f58575f848152602082815260408083206001600160a01b03871684529091529020805460ff19166001179055611f0e3390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506105cc565b5f9150506105cc565b5f61101e836001600160a01b0384166128b5565b816001600160401b03165f03611f9e5760405163ad617a0b60e01b815260040160405180910390fd5b806001600160401b03165f03611fc657604051628faa1f60e61b815260040160405180910390fd5b6040518060800160405280856001600160401b03168152602001846001600160401b03168152602001836001600160401b03168152602001826001600160401b03168152506120327f000000000000000000000000000000000000000000000000000000000000000090565b81516001919091018054602084015160408501516060909501516001600160401b03908116600160c01b026001600160c01b03968216600160801b02969096166fffffffffffffffffffffffffffffffff928216600160401b026001600160801b0319909416919095161791909117169190911791909117905550505050565b815162ffffff165f036120d857604051634632571560e01b815260040160405180910390fd5b5f82602001516001600160a01b031663a70c70e46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612119573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061213d919061333a565b9050806001600160401b0316826001600160401b0316106121715760405163b9aa612760e01b815260040160405180910390fd5b8183604001906001600160401b031690816001600160401b0316815250505f83602001516001600160a01b03166315dae03e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121d0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121f4919061333a565b90507f00000000000000000000000000000000000000000000000000000000000000008103612332576020840151604051630bc5f72160e31b81526001600160401b03851660048201526001600160a01b03821690635e2fb90890602401602060405180830381865afa15801561226d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612291919061335c565b151560a08601526040516365c14dc760e01b81526001600160401b03851660048201525f906001600160a01b038316906365c14dc7906024016101e060405180830381865afa1580156122e6573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061230a919061338a565b6101808101516001600160a01b031660608801525163ffffffff16608087015250610abf9050565b6020840151604051632695a60f60e21b81526001600160401b03851660048201525f60248201526001600160a01b03821690639a56983c906044015f60405180830381865afa158015612387573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526123ae91908101906134b3565b6001600160401b031660808b015250506001600160a01b0316606088015250151560a08601525050505050565b5f9081527f00000000000000000000000000000000000000000000000000000000000000006020526040902090565b806001600160401b0316826001600160401b0316111561243d5760405163c7f544bb60e01b815260040160405180910390fd5b826001600160401b0316816001600160401b03161015806124705750826001600160401b0316826001600160401b031610155b15610af857604051637ba485c560e01b815260040160405180910390fd5b5f61249884611057565b9050806001600160401b03165f036124c357604051630dbfe5fd60e31b815260040160405180910390fd5b5f6124ce8484612f6a565b6124d9906001612f89565b9050816001600160401b0316816001600160401b03161115610d5957604051631d1f75fb60e31b815260040160405180910390fd5b6040518060400160405280836001600160401b03168152602001826001600160401b031681525061253e846123db565b815160019190910180546020909301516001600160401b03908116600160401b026001600160801b0319909416921691909117919091179055505050565b6125868282610f2f565b6125b95760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b5f5f5160206135e35f395f51905f526125d68484610f2f565b15611f58575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506105cc565b5f61101e836001600160a01b038416612901565b5f5160206136035f395f51905f525460ff1661148257604051638dfc202b60e01b815260040160405180910390fd5b612681612a0f565b61268a826123db565b6040805160808101825282546001600160a01b031681528151808301835260018401546001600160401b038082168352600160401b918290048116602080850191909152808501939093528451606080820187526002880154808416835293840490921681850152600160801b90920460ff161515828601528385019190915283519182019093526003840180549294938501928290829061272b90612fbc565b80601f016020809104026020016040519081016040528092919081815260200182805461275790612fbc565b80156127a25780601f10612779576101008083540402835291602001916127a2565b820191905f5260205f20905b81548152906001019060200180831161278557829003601f168201915b5050509190925250505090525092915050565b5f825f0182815481106127ca576127ca61354c565b905f5260205f200154905092915050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612838573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f059190613560565b6060815f018054806020026020016040519081016040528092919081815260200182805480156128a957602002820191905f5260205f20905b815481526020019060010190808311612895575b50505050509050919050565b5f8181526001830160205260408120546128fa57508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556105cc565b505f6105cc565b5f8181526001830160205260408120548015611f58575f61292360018361357b565b85549091505f906129369060019061357b565b9050808214612995575f865f0182815481106129545761295461354c565b905f5260205f200154905080875f0184815481106129745761297461354c565b5f918252602080832090910192909255918252600188019052604090208390555b85548690806129a6576129a661358e565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506105cc565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180608001604052805f6001600160a01b03168152602001612a5660405180604001604052805f6001600160401b031681526020015f6001600160401b031681525090565b8152604080516060810182525f8082526020828101829052928201529101908152602001612a906040518060200160405280606081525090565b905290565b5f60208284031215612aa5575f5ffd5b81356001600160e01b03198116811461101e575f5ffd5b6001600160a01b0381168114610ce4575f5ffd5b6001600160401b0381168114610ce4575f5ffd5b5f5f5f5f5f60a08688031215612af8575f5ffd5b8535612b0381612abc565b94506020860135612b1381612ad0565b93506040860135612b2381612ad0565b92506060860135612b3381612ad0565b91506080860135612b4381612ad0565b809150509295509295909350565b62ffffff81168114610ce4575f5ffd5b5f5f60408385031215612b72575f5ffd5b8235612b7d81612b51565b91506020830135612b8d81612ad0565b809150509250929050565b5f60208284031215612ba8575f5ffd5b813561101e81612b51565b5f60208284031215612bc3575f5ffd5b5035919050565b5f5f5f5f5f5f5f60c0888a031215612be0575f5ffd5b8735612beb81612b51565b96506020880135612bfb81612ad0565b95506040880135612c0b81612abc565b94506060880135612c1b81612ad0565b93506080880135612c2b81612ad0565b925060a08801356001600160401b03811115612c45575f5ffd5b8801601f81018a13612c55575f5ffd5b80356001600160401b03811115612c6a575f5ffd5b8a6020828401011115612c7b575f5ffd5b602082019350809250505092959891949750929550565b5f5f60408385031215612ca3575f5ffd5b823591506020830135612b8d81612abc565b5f60208284031215612cc5575f5ffd5b813561101e81612abc565b5f81516020845280518060208601528060208301604087015e5f604082870101526040601f19601f8301168601019250505092915050565b62ffffff851681526001600160401b03841660208201528215156040820152608060608201526001600160a01b0382511660808201525f60208301516001600160401b0381511660a08401526001600160401b0360208201511660c08401525060408301516001600160401b0381511660e08401526001600160401b036020820151166101008401526040810151151561012084015250606083015160e0610140840152612dba610160840182612cd0565b979650505050505050565b5f5f5f5f60808587031215612dd8575f5ffd5b8435612de381612ad0565b93506020850135612df381612ad0565b92506040850135612e0381612ad0565b91506060850135612e1381612ad0565b939692955090935050565b5f5f5f60608486031215612e30575f5ffd5b8335612e3b81612b51565b92506020840135612e4b81612ad0565b91506040840135612e5b81612abc565b809150509250925092565b5f5f60408385031215612e77575f5ffd5b50508035926020909101359150565b8015158114610ce4575f5ffd5b5f5f5f5f60808587031215612ea6575f5ffd5b8435612eb181612b51565b93506020850135612df381612e86565b602080825282518282018190525f918401906040840190835b81811015612f015783516001600160a01b0316835260209384019390920191600101612eda565b509095945050505050565b5f5f60408385031215612f1d575f5ffd5b8235612b7d81612ad0565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b0382811682821603908111156105cc576105cc612f56565b6001600160401b0381811683821601908111156105cc576105cc612f56565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680612fd057607f821691505b602082108103612fee57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115610af857805f5260205f20601f840160051c810160208510156130195750805b601f840160051c820191505b81811015610d59575f8155600101613025565b81516001600160401b0381111561305157613051612fa8565b6130658161305f8454612fbc565b84612ff4565b6020601f821160018114613097575f83156130805750848201515b5f19600385901b1c1916600184901b178455610d59565b5f84815260208120601f198516915b828110156130c657878501518255602094850194600190920191016130a6565b50848210156130e357868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6040516101a081016001600160401b038111828210171561311557613115612fa8565b60405290565b6040516101e081016001600160401b038111828210171561311557613115612fa8565b8051611b9c81612b51565b8051611b9c81612abc565b805161ffff81168114611b9c575f5ffd5b805160ff81168114611b9c575f5ffd5b5f82601f830112613184575f5ffd5b81516001600160401b0381111561319d5761319d612fa8565b604051601f8201601f19908116603f011681016001600160401b03811182821017156131cb576131cb612fa8565b6040528181528382016020018510156131e2575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b8051611b9c81612ad0565b5f60208284031215613219575f5ffd5b81516001600160401b0381111561322e575f5ffd5b82016101a08185031215613240575f5ffd5b6132486130f2565b6132518261313e565b815261325f60208301613149565b602082015261327060408301613154565b604082015261328160608301613154565b606082015261329260808301613154565b60808201526132a360a08301613165565b60a082015260c08201516001600160401b038111156132c0575f5ffd5b6132cc86828501613175565b60c0830152506132de60e083016131fe565b60e0820152610100828101519082015261012080830151908201526133066101408301613154565b61014082015261331961016083016131fe565b61016082015261332c61018083016131fe565b610180820152949350505050565b5f6020828403121561334a575f5ffd5b5051919050565b8051611b9c81612e86565b5f6020828403121561336c575f5ffd5b815161101e81612e86565b805163ffffffff81168114611b9c575f5ffd5b5f6101e082840312801561339c575f5ffd5b506133a561311b565b6133ae83613377565b81526133bc60208401613377565b60208201526133cd60408401613377565b60408201526133de60608401613377565b60608201526133ef60808401613377565b608082015261340060a08401613377565b60a082015261341160c08401613377565b60c082015261342260e08401613165565b60e08201526134346101008401613377565b6101008201526134476101208401613377565b61012082015261345a6101408401613149565b61014082015261346d6101608401613149565b6101608201526134806101808401613149565b6101808201526134936101a08401613149565b6101a08201526134a66101c08401613351565b6101c08201529392505050565b5f5f5f5f5f5f60c087890312156134c8575f5ffd5b86516134d381612e86565b60208801519096506001600160401b038111156134ee575f5ffd5b6134fa89828a01613175565b955050604087015161350b81612abc565b606088015190945061351c81612ad0565b608088015190935061352d81612ad0565b60a088015190925061353e81612ad0565b809150509295509295509295565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613570575f5ffd5b815161101e81612abc565b818103818111156105cc576105cc612f56565b634e487b7160e01b5f52603160045260245ffdfec1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000794daa56950487582951e8db2fdbcbee68c2223c65641d0aa02a3afc64f9a86f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a164736f6c634300081c000a00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000", + "nonce": "0x49", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xc6a0779a406a82c2d3df8d61d264958464f943189959040a72636e11a79c6725", + "transactionType": "CALL", + "contractName": "OssifiableProxy", + "contractAddress": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "function": "proxy__upgradeTo(address)", + "arguments": [ + "0x76773E5A432113238be489F9A7Bf716e8dC4a015" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "gas": "0xbcc3", + "value": "0x0", + "input": "0x3ebdd0eb00000000000000000000000076773e5a432113238be489f9a7bf716e8dc4a015", + "nonce": "0x4a", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x19fd698", + "logs": [ + { + "address": "0x73cca4dd9e58fa3abca22d55ee81ae613d958980", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0xca7a99583f33e0480dd1506d4616b0fa8770cf7d8a37cd9f258ad7f9aaa66582", + "transactionIndex": "0x9", + "logIndex": "0xf7", + "removed": false + } + ], + "logsBloom": "0x00000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xca7a99583f33e0480dd1506d4616b0fa8770cf7d8a37cd9f258ad7f9aaa66582", + "transactionIndex": "0x9", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "gasUsed": "0x2b0f7c", + "effectiveGasPrice": "0xf4afd", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": null, + "contractAddress": "0x73cca4dd9e58fa3abca22d55ee81ae613d958980" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x1aeed05", + "logs": [ + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x00000000000000000000000073cca4dd9e58fa3abca22d55ee81ae613d958980" + ], + "data": "0x", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xf8", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef" + ], + "data": "0x", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xf9", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x794daa56950487582951e8db2fdbcbee68c2223c65641d0aa02a3afc64f9a86f", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef" + ], + "data": "0x", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xfa", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef" + ], + "data": "0x", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xfb", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef" + ], + "data": "0x", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xfc", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0x84d25455db084cf777a0a4a00fcc28946b3a2ebc5cc866ef8820ab8571dbdd36" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xfd", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xfe", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xff", + "removed": false + } + ], + "logsBloom": "0x00000004000000000040000000000000400000000000000000000000000000000000000000000000000000000000000000040000000000000000080000000000000200000000000000000000400002000000000000000000000000000200000000000000028400000000000000000800000000800000000000010000000000000000000000002000000800000000001000080000000080000000000000800000000000000000000060000000000000000000000000000200001000000000000020000020000000000000200000000000000000000404000100000000040020018000000000000000000000000000000000040000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "gasUsed": "0xf166d", + "effectiveGasPrice": "0xf4afd", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": null, + "contractAddress": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x1af28aa", + "logs": [ + { + "address": "0x7c178b9b797c6ea6776a784c22a0f95a79385c9b", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + "blockHash": "0x3dfa399aa60c13bbfbe8f1cc9cd9cde07700ee525ec96fb788a6419a17c6ec28", + "blockNumber": "0x30ece5", + "blockTimestamp": "0x6792c828", + "transactionHash": "0x138cad03fbe5a1d4f10a6765b4328249858670248d32bb3f8fbbde49ac17a339", + "transactionIndex": "0x9", + "logIndex": "0x10d", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000080000000000000000000000000000000000010000000000000000000000000004000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x138cad03fbe5a1d4f10a6765b4328249858670248d32bb3f8fbbde49ac17a339", + "transactionIndex": "0x9", + "blockHash": "0x3dfa399aa60c13bbfbe8f1cc9cd9cde07700ee525ec96fb788a6419a17c6ec28", + "blockNumber": "0x30ece5", + "gasUsed": "0x2b6865", + "effectiveGasPrice": "0xf424f", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": null, + "contractAddress": "0x7c178b9b797c6ea6776a784c22a0f95a79385c9b" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x1e030d3", + "logs": [ + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000007c178b9b797c6ea6776a784c22a0f95a79385c9b" + ], + "data": "0x", + "blockHash": "0x3dfa399aa60c13bbfbe8f1cc9cd9cde07700ee525ec96fb788a6419a17c6ec28", + "blockNumber": "0x30ece5", + "blockTimestamp": "0x6792c828", + "transactionHash": "0xde2a6f7ad4ba810948e3c1e417e3e6520a8828267a594d759ad12d0802fde233", + "transactionIndex": "0xd", + "logIndex": "0x114", + "removed": false + } + ], + "logsBloom": "0x00000000000000000040000000000000400000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000020000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xde2a6f7ad4ba810948e3c1e417e3e6520a8828267a594d759ad12d0802fde233", + "transactionIndex": "0xd", + "blockHash": "0x3dfa399aa60c13bbfbe8f1cc9cd9cde07700ee525ec96fb788a6419a17c6ec28", + "blockNumber": "0x30ece5", + "gasUsed": "0x8112", + "effectiveGasPrice": "0xf424f", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x1b9df0d", + "logs": [ + { + "address": "0x0442fb2c8607d04d6ba496ea8990da7bd7ec25fc", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + "blockHash": "0x961d98bea6b208d1fd92cd1951541acbbef1728f836c520ee543065e993dd20d", + "blockNumber": "0x31650f", + "blockTimestamp": "0x6798d2a8", + "transactionHash": "0xfb7ba8b9366bd8189498e164cabf5317c199115d94919cbf432de63e185af960", + "transactionIndex": "0x9", + "logIndex": "0xff", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000080000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xfb7ba8b9366bd8189498e164cabf5317c199115d94919cbf432de63e185af960", + "transactionIndex": "0x9", + "blockHash": "0x961d98bea6b208d1fd92cd1951541acbbef1728f836c520ee543065e993dd20d", + "blockNumber": "0x31650f", + "gasUsed": "0x2b7427", + "effectiveGasPrice": "0xf424b", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": null, + "contractAddress": "0x0442fb2c8607d04d6ba496ea8990da7bd7ec25fc" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x1ba601f", + "logs": [ + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000000442fb2c8607d04d6ba496ea8990da7bd7ec25fc" + ], + "data": "0x", + "blockHash": "0x961d98bea6b208d1fd92cd1951541acbbef1728f836c520ee543065e993dd20d", + "blockNumber": "0x31650f", + "blockTimestamp": "0x6798d2a8", + "transactionHash": "0xbf3baa41f166f615fc2370f47f338b6b5e56d27f70d9fdeb51efdf8cd61aa2be", + "transactionIndex": "0xa", + "logIndex": "0x100", + "removed": false + } + ], + "logsBloom": "0x00000000000000000040000000000000400000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000020000000000000000000000000000000000000000010000000000000000000000000000000001000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xbf3baa41f166f615fc2370f47f338b6b5e56d27f70d9fdeb51efdf8cd61aa2be", + "transactionIndex": "0xa", + "blockHash": "0x961d98bea6b208d1fd92cd1951541acbbef1728f836c520ee543065e993dd20d", + "blockNumber": "0x31650f", + "gasUsed": "0x8112", + "effectiveGasPrice": "0xf424b", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xa94462", + "logs": [ + { + "address": "0x82cfae30820b4249acd6315c90a4d0a9fef5a99a", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + "blockHash": "0xa2b7ec82cf05335ee76b5a5f424adbfbd6f019ff8dd5b0ef90cb5771556073bb", + "blockNumber": "0x317382", + "blockTimestamp": "0x67998f30", + "transactionHash": "0xfa237667dbb4f06775db358bbb699b0170861becbb005617df009b5138c46164", + "transactionIndex": "0x11", + "logIndex": "0x25", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000080000000000000000000000000800000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xfa237667dbb4f06775db358bbb699b0170861becbb005617df009b5138c46164", + "transactionIndex": "0x11", + "blockHash": "0xa2b7ec82cf05335ee76b5a5f424adbfbd6f019ff8dd5b0ef90cb5771556073bb", + "blockNumber": "0x317382", + "gasUsed": "0x2e589f", + "effectiveGasPrice": "0xf4251", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": null, + "contractAddress": "0x82cfae30820b4249acd6315c90a4d0a9fef5a99a" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xa9c574", + "logs": [ + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x00000000000000000000000082cfae30820b4249acd6315c90a4d0a9fef5a99a" + ], + "data": "0x", + "blockHash": "0xa2b7ec82cf05335ee76b5a5f424adbfbd6f019ff8dd5b0ef90cb5771556073bb", + "blockNumber": "0x317382", + "blockTimestamp": "0x67998f30", + "transactionHash": "0x867f5b6a3545347409a33493a0b80ba7e75ed96b54d7b7b56eff08ea9609a54f", + "transactionIndex": "0x12", + "logIndex": "0x26", + "removed": false + } + ], + "logsBloom": "0x00000000000000000040000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000010000000000000002000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x867f5b6a3545347409a33493a0b80ba7e75ed96b54d7b7b56eff08ea9609a54f", + "transactionIndex": "0x12", + "blockHash": "0xa2b7ec82cf05335ee76b5a5f424adbfbd6f019ff8dd5b0ef90cb5771556073bb", + "blockNumber": "0x317382", + "gasUsed": "0x8112", + "effectiveGasPrice": "0xf4251", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x6323fc", + "logs": [ + { + "address": "0x76773e5a432113238be489f9a7bf716e8dc4a015", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + "blockHash": "0x57c3d6218f235bc92540419428d3c6be61a1166a2dfa83fb8422ec00f53420bb", + "blockNumber": "0x317e50", + "blockTimestamp": "0x679a1bdc", + "transactionHash": "0x7bec86c8525d9afe2a2ae2cce7526d114800521780f991aad20a53b06e363dee", + "transactionIndex": "0x11", + "logIndex": "0x2d", + "removed": false + } + ], + "logsBloom": "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000080000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x7bec86c8525d9afe2a2ae2cce7526d114800521780f991aad20a53b06e363dee", + "transactionIndex": "0x11", + "blockHash": "0x57c3d6218f235bc92540419428d3c6be61a1166a2dfa83fb8422ec00f53420bb", + "blockNumber": "0x317e50", + "gasUsed": "0x2f15e2", + "effectiveGasPrice": "0xf4250", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": null, + "contractAddress": "0x76773e5a432113238be489f9a7bf716e8dc4a015" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x63a50e", + "logs": [ + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x00000000000000000000000076773e5a432113238be489f9a7bf716e8dc4a015" + ], + "data": "0x", + "blockHash": "0x57c3d6218f235bc92540419428d3c6be61a1166a2dfa83fb8422ec00f53420bb", + "blockNumber": "0x317e50", + "blockTimestamp": "0x679a1bdc", + "transactionHash": "0xc6a0779a406a82c2d3df8d61d264958464f943189959040a72636e11a79c6725", + "transactionIndex": "0x12", + "logIndex": "0x2e", + "removed": false + } + ], + "logsBloom": "0x00000000000000000040000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000010010000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xc6a0779a406a82c2d3df8d61d264958464f943189959040a72636e11a79c6725", + "transactionIndex": "0x12", + "blockHash": "0x57c3d6218f235bc92540419428d3c6be61a1166a2dfa83fb8422ec00f53420bb", + "blockNumber": "0x317e50", + "gasUsed": "0x8112", + "effectiveGasPrice": "0xf4250", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1738116938, + "chain": 17000, + "commit": "aaa0e97" +} diff --git a/artifacts/latest/.gitignore b/artifacts/latest/.gitignore new file mode 100644 index 0000000..9749182 --- /dev/null +++ b/artifacts/latest/.gitignore @@ -0,0 +1,2 @@ +# latest deployment configs after broadcasting on the network +*.json diff --git a/artifacts/local/.gitignore b/artifacts/local/.gitignore new file mode 100644 index 0000000..9749182 --- /dev/null +++ b/artifacts/local/.gitignore @@ -0,0 +1,2 @@ +# latest deployment configs after broadcasting on the network +*.json diff --git a/artifacts/mainnet/.gikeep b/artifacts/mainnet/.gikeep new file mode 100644 index 0000000..e69de29 diff --git a/foundry.toml b/foundry.toml index f7e676f..47cafcf 100644 --- a/foundry.toml +++ b/foundry.toml @@ -17,8 +17,12 @@ cache_path = "cache" block_gas_limit = 30_000_000 fuzz = { runs = 256 } -# gas_reports = [ -# ] +gas_reports = [ + "CCR", + "CCRDataStorage", + "OssifiableProxy" +] + fs_permissions = [ { access = "read-write", path = "./out" }, @@ -26,6 +30,8 @@ fs_permissions = [ # { access = "read", path = "./test/fixtures" }, ] +ignored_warnings_from = ["test/OssifiableProxy.t.sol"] + [profile.ci] verbosity = 3 fuzz = { runs = 10_000, max_test_rejects = 2_000_000 } diff --git a/package.json b/package.json index ece2d42..856ef9d 100644 --- a/package.json +++ b/package.json @@ -8,15 +8,20 @@ "private": true, "scripts": { "lint:solhint": "solhint './src/**/*.sol'", - "lint:check": "prettier --check **.sol && yarn lint:solhint", - "lint:fix": "prettier --write **.sol" + "lint:check": "forge fmt --check && yarn lint:solhint", + "lint:fix": "forge fmt", + "generate:diffyscan": "node script/generateDiffyscanContracts.js", + "scc:report": "scc src --sort names --no-cocomo --exclude-dir interfaces --by-file --format wide > scc-report.txt", + "scm:report": "solidity-code-metrics $(tree -f -i -I '*interfaces*' | grep '^./src.*sol' | xargs ls -d 2>/dev/null) > scm-report.md" }, "devDependencies": { "husky": "^9.1.7", - "lint-staged": "^15.3.0", + "lint-staged": "^15.4.2", "prettier": "^3.4.2", "prettier-plugin-solidity": "^1.4.2", - "solhint": "5.0.4" + "solhint": "5.0.5", + "solhint-plugin-lido-csm": "https://github.com/lidofinance/solhint-plugin-lido-csm.git#0.3.3", + "solidity-code-metrics": "^0.0.28" }, "lint-staged": { "*": "prettier --ignore-unknown --write", diff --git a/script/DeployBase.s.sol b/script/DeployBase.s.sol deleted file mode 100644 index e82b42c..0000000 --- a/script/DeployBase.s.sol +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Lido -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.28; - -import {Script, console} from "forge-std/Script.sol"; -// import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; -import {OssifiableProxy} from "../src/lib/proxy/OssifiableProxy.sol"; -import {CredibleCommitmentCurationProvider} from "../src/CredibleCommitmentCurationProvider.sol"; - -import {JsonObj, Json} from "./utils/Json.sol"; - -struct DeployParams { - address lidoLocatorAddress; - bytes32 csModuleType; - address proxyAdmin; - address committeeAddress; - uint64 optInMinDurationBlocks; - uint64 optOutDelayDurationBlocks; - uint64 defaultOperatorMaxValidators; - uint64 defaultBlockGasLimit; -} - -abstract contract DeployBase is Script { - DeployParams internal config; - string internal artifactDir; - string internal chainName; - uint256 internal chainId; - - address internal deployer; - uint256 internal pk; - CredibleCommitmentCurationProvider public cccp; - - error ChainIdMismatch(uint256 actual, uint256 expected); - - constructor(string memory _chainName, uint256 _chainId) { - chainName = _chainName; - chainId = _chainId; - } - - function _setUp() internal {} - - function run() external virtual { - if (chainId != block.chainid) { - revert ChainIdMismatch({actual: block.chainid, expected: chainId}); - } - - artifactDir = vm.envOr("ARTIFACTS_DIR", string("./artifacts/")); - pk = vm.envUint("DEPLOYER_PRIVATE_KEY"); - deployer = vm.addr(pk); - vm.label(deployer, "DEPLOYER"); - - vm.startBroadcast(pk); - { - address cccpImpl = - address(new CredibleCommitmentCurationProvider(config.lidoLocatorAddress, config.csModuleType)); - console.log("CCCP impl address", address(cccpImpl)); - - cccp = CredibleCommitmentCurationProvider(_deployProxy(config.proxyAdmin, address(cccpImpl))); - cccp.initialize({ - committeeAddress: config.committeeAddress, - optInMinDurationBlocks: config.optInMinDurationBlocks, - optOutDelayDurationBlocks: config.optOutDelayDurationBlocks, - defaultOperatorMaxValidators: config.defaultOperatorMaxValidators, - defaultBlockGasLimit: config.defaultBlockGasLimit - }); - console.log("CCCP address", address(cccp)); - - JsonObj memory deployJson = Json.newObj(); - deployJson.set("ChainId", chainId); - deployJson.set("CCCPImpl", cccpImpl); - deployJson.set("CCCP", address(cccp)); - deployJson.set("LidoLocator", config.lidoLocatorAddress); - deployJson.set("DeployParams", abi.encode(config)); - vm.writeJson(deployJson.str, _deployJsonFilename()); - } - - vm.stopBroadcast(); - } - - function _deployProxy(address admin, address implementation) internal returns (address) { - OssifiableProxy proxy = - new OssifiableProxy({implementation_: implementation, data_: new bytes(0), admin_: admin}); - - return address(proxy); - } - - function _deployJsonFilename() internal view returns (string memory) { - return string(abi.encodePacked(artifactDir, "deploy-", chainName, ".json")); - } -} diff --git a/script/DeployBase.sol b/script/DeployBase.sol new file mode 100644 index 0000000..cfbddcb --- /dev/null +++ b/script/DeployBase.sol @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {ScriptInit} from "./ScriptInit.sol"; +import {JsonObj, Json} from "./utils/Json.sol"; + +import {OssifiableProxy} from "../src/lib/proxy/OssifiableProxy.sol"; +import {CCR} from "../src/CCR.sol"; + +struct DeployParams { + address lidoLocatorAddress; + bytes32 csModuleType; + address proxyAdmin; + address committeeAddress; + uint64 optInDelayBlocks; + uint64 optOutDelayBlocks; + uint64 defaultOperatorMaxKeys; + uint64 defaultBlockGasLimit; +} + +abstract contract DeployBase is ScriptInit { + DeployParams public params; + CCR public ccr; + + constructor(string memory _chainName, uint256 _chainId) ScriptInit(_chainName, _chainId) {} + + function run() external virtual { + init(); + + vm.startBroadcast(pk); + { + // deploy new CCR implementation + CCR ccrImpl = _deployImplementation(params); + + ccr = CCR( + _deployProxy( + params.proxyAdmin, + address(ccrImpl), + abi.encodeCall( + CCR.initialize, + ( + params.committeeAddress, + params.optInDelayBlocks, + params.optOutDelayBlocks, + params.defaultOperatorMaxKeys, + params.defaultBlockGasLimit + ) + ) + ) + ); + + JsonObj memory deployJson = Json.newObj(); + deployJson.set("ChainId", chainId); + deployJson.set("CCRImpl", address(ccrImpl)); + deployJson.set("CCR", address(ccr)); + deployJson.set("LidoLocator", params.lidoLocatorAddress); + deployJson.set("DeployParams", abi.encode(params)); + vm.writeJson(deployJson.str, _deployJsonFilename()); + } + + vm.stopBroadcast(); + } + + /// @dev can be overridden to customize the upgrade process + function _deployImplementation(DeployParams memory _params) internal virtual returns (CCR) { + return new CCR(_params.lidoLocatorAddress, _params.csModuleType); + } + + function _deployProxy(address _admin, address _impl, bytes memory _data) internal returns (address) { + OssifiableProxy proxy = new OssifiableProxy({implementation_: _impl, data_: _data, admin_: _admin}); + + return address(proxy); + } +} diff --git a/script/DeployHolesky.s.sol b/script/DeployHolesky.s.sol index 966ac0d..620b9ed 100644 --- a/script/DeployHolesky.s.sol +++ b/script/DeployHolesky.s.sol @@ -3,23 +3,23 @@ pragma solidity 0.8.28; -import {DeployBase} from "./DeployBase.s.sol"; +import {DeployBase} from "./DeployBase.sol"; contract DeployHolesky is DeployBase { constructor() DeployBase("holesky", 17000) { // implementation constants - config.lidoLocatorAddress = 0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8; - config.csModuleType = "community-onchain-v1"; - config.defaultOperatorMaxValidators = 100; - config.defaultBlockGasLimit = 1000000; + params.lidoLocatorAddress = 0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8; + params.csModuleType = "community-onchain-v1"; + params.defaultOperatorMaxKeys = 100; + params.defaultBlockGasLimit = 1000000; // proxy - config.proxyAdmin = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA + params.proxyAdmin = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA // initial parameters - config.committeeAddress = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA - config.optInMinDurationBlocks = 32; - config.optOutDelayDurationBlocks = 64; + params.committeeAddress = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA + params.optInDelayBlocks = 32; + params.optOutDelayBlocks = 64; _setUp(); } diff --git a/script/DeployMainnet.s.sol b/script/DeployMainnet.s.sol index adae97e..d03100f 100644 --- a/script/DeployMainnet.s.sol +++ b/script/DeployMainnet.s.sol @@ -3,23 +3,23 @@ pragma solidity 0.8.28; -import {DeployBase} from "./DeployBase.s.sol"; +import {DeployBase} from "./DeployBase.sol"; contract DeployMainnet is DeployBase { constructor() DeployBase("mainnet", 1) { // implementation constants - config.lidoLocatorAddress = 0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb; - config.csModuleType = "community-onchain-v1"; - config.defaultOperatorMaxValidators = 100; - config.defaultBlockGasLimit = 1000000; + params.lidoLocatorAddress = 0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb; + params.csModuleType = "community-onchain-v1"; + params.defaultOperatorMaxKeys = 1000; + params.defaultBlockGasLimit = 3000000; // proxy - config.proxyAdmin = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA + params.proxyAdmin = 0x0000000000000000000000000000000000000000; // Dev team EOA // initial parameters - config.committeeAddress = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA - config.optInMinDurationBlocks = 32; - config.optOutDelayDurationBlocks = 64; + params.committeeAddress = 0x0000000000000000000000000000000000000000; // Dev team EOA + params.optInDelayBlocks = 0; + params.optOutDelayBlocks = 64; _setUp(); } diff --git a/script/ScriptInit.sol b/script/ScriptInit.sol new file mode 100644 index 0000000..93c6d83 --- /dev/null +++ b/script/ScriptInit.sol @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {Script} from "forge-std/Script.sol"; + +abstract contract ScriptInit is Script { + string internal artifactDir; + string internal chainName; + uint256 internal chainId; + address internal deployer; + uint256 internal pk; + + error ChainIdMismatch(uint256 actual, uint256 expected); + + constructor(string memory _chainName, uint256 _chainId) { + chainName = _chainName; + chainId = _chainId; + } + + function _setUp() internal virtual {} + + function init() internal virtual { + if (chainId != block.chainid) { + revert ChainIdMismatch({actual: block.chainid, expected: chainId}); + } + + artifactDir = vm.envOr("ARTIFACTS_DIR", string("./artifacts/local/")); + pk = vm.envUint("DEPLOYER_PRIVATE_KEY"); + deployer = vm.addr(pk); + vm.label(deployer, "DEPLOYER"); + } + + function _deployJsonFilename() internal view returns (string memory) { + return string(abi.encodePacked(artifactDir, "deploy-", chainName, ".json")); + } +} diff --git a/script/UpgradeBase.sol b/script/UpgradeBase.sol new file mode 100644 index 0000000..99ab857 --- /dev/null +++ b/script/UpgradeBase.sol @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {ScriptInit} from "./ScriptInit.sol"; +import {JsonObj, Json} from "./utils/Json.sol"; + +import {OssifiableProxy} from "../src/lib/proxy/OssifiableProxy.sol"; +import {CCR} from "../src/CCR.sol"; +import {DeployParams} from "./DeployBase.sol"; + +abstract contract UpgradeBase is ScriptInit { + DeployParams internal params; + CCR public ccr; + + error ArtifactsChainIdMismatch(uint256 actual, uint256 expected); + + constructor(string memory _chainName, uint256 _chainId) ScriptInit(_chainName, _chainId) {} + + function run() external { + init(); + + string memory artifactsPath = vm.envOr("DEPLOY_CONFIG", string("")); + uint256 artifactsChainId; + (artifactsChainId, ccr, params) = parseArtifacts(artifactsPath); + + if (chainId != artifactsChainId) { + revert ArtifactsChainIdMismatch({actual: artifactsChainId, expected: chainId}); + } + + vm.startBroadcast(pk); + { + OssifiableProxy proxy = OssifiableProxy(payable(address(ccr))); + + // deploy new CCR implementation with the same LidoLocator and CSModuleType + CCR ccrImpl = _deployImplementation(params); + _upgradeProxy(proxy, ccrImpl, params); + + JsonObj memory deployJson = Json.newObj(); + deployJson.set("ChainId", chainId); + deployJson.set("CCRImpl", address(ccrImpl)); + deployJson.set("CCR", address(ccr)); + deployJson.set("LidoLocator", params.lidoLocatorAddress); + deployJson.set("DeployParams", abi.encode(params)); + vm.writeJson(deployJson.str, _deployJsonFilename()); + } + + vm.stopBroadcast(); + } + + function parseArtifacts(string memory artifactsPath) internal view returns (uint256, CCR, DeployParams memory) { + string memory artifactsJson = vm.readFile(artifactsPath); + + return ( + vm.parseJsonUint(artifactsJson, ".ChainId"), + CCR(vm.parseJsonAddress(artifactsJson, ".CCR")), + abi.decode(vm.parseJsonBytes(artifactsJson, ".DeployParams"), (DeployParams)) + ); + } + + /// @dev can be overridden to customize the upgrade process + function _deployImplementation(DeployParams memory _params) internal virtual returns (CCR) { + return new CCR(_params.lidoLocatorAddress, _params.csModuleType); + } + + /// @dev can be overridden to customize the upgrade process + function _upgradeProxy(OssifiableProxy _proxy, CCR _impl, DeployParams memory _params) internal virtual { + // upgrade proxy to new CCR implementation + _proxy.proxy__upgradeTo(address(_impl)); + // silent warning: unused variable + _params; + + // example of calling a function on the new implementation + // proxy.proxy__upgradeToAndCall(address(impl), abi.encodeCall(impl.initialize_v2, ())); + } +} diff --git a/script/UpgradeHolesky.s.sol b/script/UpgradeHolesky.s.sol new file mode 100644 index 0000000..0d1fc6d --- /dev/null +++ b/script/UpgradeHolesky.s.sol @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {UpgradeBase} from "./UpgradeBase.sol"; + +contract UpgradeHolesky is UpgradeBase { + constructor() UpgradeBase("holesky", 17000) { + // implementation constants + params.lidoLocatorAddress = 0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8; + params.csModuleType = "community-onchain-v1"; + params.defaultOperatorMaxKeys = 100; + params.defaultBlockGasLimit = 1000000; + + // proxy + params.proxyAdmin = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA + + // initial parameters + params.committeeAddress = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA + params.optInDelayBlocks = 32; + params.optOutDelayBlocks = 64; + + _setUp(); + } +} diff --git a/script/generateDiffyscanContracts.js b/script/generateDiffyscanContracts.js new file mode 100644 index 0000000..d8a7037 --- /dev/null +++ b/script/generateDiffyscanContracts.js @@ -0,0 +1,34 @@ +const fs = require("fs"); +const path = require("path"); + +const ARTIFACTS_DIR = "artifacts"; + +function readJsonFile(path) { + return JSON.parse(fs.readFileSync(path)); +} + +function main() { + let result = {}; + const possibleArtifacts = fs.readdirSync(ARTIFACTS_DIR); + if (!possibleArtifacts.includes(process.argv[2])) { + throw new Error( + "Invalid arg. Possible values: " + possibleArtifacts.join(", "), + ); + } + const transactions = readJsonFile( + path.join(ARTIFACTS_DIR, process.argv[2], "transactions.json"), + ).transactions; + transactions + .filter((tx) => tx.transactionType === "CREATE") + .forEach((tx) => { + result[tx.contractAddress] = tx.contractName; + }); + console.log(JSON.stringify(result, null, 2)); +} + +try { + main(); +} catch (error) { + console.error(error); + process.exitCode = 1; +} diff --git a/src/CCR.sol b/src/CCR.sol new file mode 100644 index 0000000..d79cb7a --- /dev/null +++ b/src/CCR.sol @@ -0,0 +1,728 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {AccessControlEnumerableUpgradeable} from + "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; +import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; + +import {CCROperatorStatesStorage} from "./lib/CCROperatorStatesStorage.sol"; +import {CCRConfigStorage} from "./lib/CCRConfigStorage.sol"; +import {ICCR} from "./interfaces/ICCR.sol"; + +import {ILidoLocator} from "./interfaces/ILidoLocator.sol"; +import {StakingModule, IStakingRouter} from "./interfaces/IStakingRouter.sol"; +import {IStakingModule} from "./interfaces/IStakingModule.sol"; +import {CSMNodeOperator, ICSModule} from "./interfaces/ICSModule.sol"; +import {ICuratedModule} from "./interfaces/ICuratedModule.sol"; + +/** + * @title CCR + * @notice CredibleCommitmentCurationProvider contract + */ +contract CCR is + ICCR, + Initializable, + CCRConfigStorage, + CCROperatorStatesStorage, + AccessControlEnumerableUpgradeable, + PausableUpgradeable +{ + struct LidoOperatorCache { + uint24 moduleId; + address moduleAddress; + uint64 operatorId; + address rewardAddress; + uint64 totalKeys; + bool isActive; + } + + struct OperatorOptInOutFlags { + bool isOptedIn; + // bool isOptedOut; + bool isOptInDelayed; + bool isOptOutDelayed; + } + + bytes32 public constant COMMITTEE_ROLE = keccak256("COMMITTEE_ROLE"); + bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE"); + bytes32 public constant RESUME_ROLE = keccak256("RESUME_ROLE"); + uint64 public constant MAX_COMMITMENTS = 64; + + ILidoLocator public immutable LIDO_LOCATOR; + // CSModule type, used for the operator's state retrieval + bytes32 internal immutable CS_MODULE_TYPE; + + event OptInSucceeded(uint256 indexed moduleId, uint256 indexed operatorId); + event OptOutRequested(uint256 indexed moduleId, uint256 indexed operatorId, bool isForced); + event UnblockOperator(uint256 indexed moduleId, uint256 indexed operatorId); + + // event KeyRangeUpdated(uint256 indexed moduleId, uint256 indexed operatorId, uint256 indexStart, uint256 indexEnd); + event OperatorCommitmentAdded( + uint256 indexed moduleId, uint256 indexed operatorId, uint256 keyIndexStart, uint256 keyIndexEnd, string rpcURL + ); + event OperatorCommitmentDeleted( + uint256 indexed moduleId, uint256 indexed operatorId, uint256 keyIndexStart, uint256 keyIndexEnd + ); + event OperatorManagerUpdated(uint256 indexed moduleId, uint256 indexed operatorId, address manager); + event OperatorDelegateAdded(uint256 indexed moduleId, uint256 indexed operatorId, bytes key); + event OperatorDelegateDeleted(uint256 indexed moduleId, uint256 indexed operatorId, bytes key); + // event RPCUrlUpdated(uint256 indexed moduleId, uint256 indexed operatorId, string rpcURL); + event ConfigUpdated( + uint256 optInDelayBlocks, + uint256 optOutDelayBlocks, + uint256 defaultOperatorMaxKeys, + uint256 defaultBlockGasLimit + ); + event ModuleConfigUpdated(uint256 indexed moduleId, bool isDisabled, uint256 operatorMaxKeys, uint64 blockGasLimit); + + error RewardAddressMismatch(); + error OperatorNotActive(); + error OperatorBlocked(); + error ModuleDisabled(); + error OperatorOptedIn(); + error OperatorOptedOut(); + error OperatorOptInNotAllowed(); + error OperatorOptOutNotAllowed(); + error OperatorOptOutInProgress(); + error KeyIndexOutOfRange(); + error KeyRangeExceedMaxKeys(); + error KeyIndexOverlapExisting(); + error KeyIndexMismatch(); + error InvalidOperatorId(); + error InvalidModuleId(); + error ZeroCommitteeAddress(); + error ZeroOperatorManagerAddress(); + error ZeroLocatorAddress(); + error CommitmentsLimitReached(); + error OptInActionDelayed(); + error OptOutActionDelayed(); + + constructor(address lidoLocator, bytes32 csModuleType) { + if (lidoLocator == address(0)) revert ZeroLocatorAddress(); + LIDO_LOCATOR = ILidoLocator(lidoLocator); + CS_MODULE_TYPE = csModuleType; + + _disableInitializers(); + } + + function initialize( + address committeeAddress, + uint64 optInDelayBlocks, + uint64 optOutDelayBlocks, + uint64 defaultOperatorMaxKeys, + uint64 defaultBlockGasLimit + ) external initializer { + if (committeeAddress == address(0)) revert ZeroCommitteeAddress(); + + __Pausable_init(); + __AccessControlEnumerable_init(); + + _grantRole(DEFAULT_ADMIN_ROLE, committeeAddress); + _grantRole(COMMITTEE_ROLE, committeeAddress); + _grantRole(PAUSE_ROLE, committeeAddress); + _grantRole(RESUME_ROLE, committeeAddress); + + _updateConfig(optInDelayBlocks, optOutDelayBlocks, defaultOperatorMaxKeys, defaultBlockGasLimit); + + ///todo: pass module Ids to disable them at the start + } + + /// @notice Resume all operations after a pause + function unpause() external onlyRole(RESUME_ROLE) { + _unpause(); + } + + /// @notice Pause all operations + function pause() external onlyRole(PAUSE_ROLE) { + _pause(); + } + + /// @notice Opt-in operator to the module + /// @dev allows a repeated optin with different manager address + function optIn( + uint24 moduleId, + uint64 operatorId, + address manager, + uint64 indexStart, + uint64 indexEnd, + string calldata rpcURL + ) external whenNotPaused { + if (manager == address(0)) revert ZeroOperatorManagerAddress(); + + LidoOperatorCache memory _c; + /// @dev correctness of moduleId and operatorId are checked inside + _loadLidoNodeOperator(_c, moduleId, operatorId); + + // check if the caller is the operator's reward address + if (msg.sender != _c.rewardAddress) { + revert RewardAddressMismatch(); + } + + // check if the operator is active in Lido module + if (!_c.isActive) { + revert OperatorNotActive(); + } + + uint256 opKey = __encOpKey(moduleId, operatorId); + if (_getIsOperatorBlocked(opKey)) { + revert OperatorBlocked(); + } + + // check if the operator is already has the state + OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); + OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); + if (flags.isOptedIn) { + revert OperatorOptedIn(); + } + _checkOptInDelayed(flags); + + // save operator state + /// @dev also checks if the proposed manager is already registered for different operator + _setOperatorManager(opKey, manager); + emit OperatorManagerUpdated(moduleId, operatorId, manager); + + /// @dev clear the previous commitments (could be expensive!) + _delOperatorAllCommitments(opKey); + + _checkAndAddCommitment(_c, opKey, indexStart, indexEnd, rpcURL); + emit OperatorCommitmentAdded(moduleId, operatorId, indexStart, indexEnd, rpcURL); + + emit OptInSucceeded(moduleId, operatorId); + + optInOutState.optInBlock = uint64(block.number); + optInOutState.optOutBlock = 0; + _setOptInOutStateWithOptInDelay(opKey, optInOutState); + } + + /// @notice Opt-out operator on behalf of the operator manager + /// @dev should be called by the operator manager address + function optOut() external whenNotPaused { + (uint256 opKey, OptInOutState memory optInOutState, OperatorOptInOutFlags memory flags) = + _checkOptInAndLoadStateByManager(msg.sender); + _checkOptInDelayed(flags); + + optInOutState.optOutBlock = uint64(block.number); + _setOptInOutStateWithOptOutDelay(opKey, optInOutState); + + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + emit OptOutRequested(moduleId, operatorId, false); + } + + /// @notice Opt-out operator on behalf of the committee + function optOut(uint24 moduleId, uint64 operatorId) external onlyRole(COMMITTEE_ROLE) { + uint256 opKey = _getOpKeyById(moduleId, operatorId); + (OptInOutState memory optInOutState,) = _checkOptInAndLoadState(opKey); + /// @dev ignore isOptOutDelayed, as the committee can force opt-out + + optInOutState.optOutBlock = uint64(block.number); + _setOptInOutStateWithOptOutDelay(opKey, optInOutState); + _setIsOperatorBlocked(opKey, true); + + emit OptOutRequested(moduleId, operatorId, true); + } + + function getOperatorDelegates(uint24 moduleId, uint64 operatorId) + external + view + returns (Delegate[] memory delegates) + { + uint256 opKey = __encOpKey(moduleId, operatorId); + delegates = _getOperatorDelegatesStorage(opKey); + } + + function addOperatorDelegate(bytes memory key) external whenNotPaused { + (uint256 opKey,,) = _checkOptInAndLoadStateByManager(msg.sender); + + /// @dev no checks on delegate key + _addOperatorDelegate(opKey, key); + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + emit OperatorDelegateAdded(moduleId, operatorId, key); + } + + function delOperatorDelegate(uint256 dIdx) external whenNotPaused { + (uint256 opKey,,) = _checkOptInAndLoadStateByManager(msg.sender); + + bytes memory key = _getOperatorDelegateStorage(opKey, dIdx).key; + + _delOperatorDelegate(opKey, dIdx); + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + emit OperatorDelegateDeleted(moduleId, operatorId, key); + } + + function getOperatorCommitments(uint24 moduleId, uint64 operatorId) + external + view + returns (Commitment[] memory commitments) + { + uint256 opKey = __encOpKey(moduleId, operatorId); + commitments = _getOperatorCommitmentsStorage(opKey); + } + + /// @notice Update the operator's keys kr + /// @dev should be called by the operator manager address + /// @dev `opt-in` action + function addOperatorCommitment(uint64 indexStart, uint64 indexEnd, string calldata rpcUrl) external whenNotPaused { + (uint256 opKey, OptInOutState memory optInOutState, OperatorOptInOutFlags memory flags) = + _checkOptInAndLoadStateByManager(msg.sender); + _checkOptInDelayed(flags); + + LidoOperatorCache memory _c; + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + _loadLidoNodeOperator(_c, moduleId, operatorId); + _checkAndAddCommitment(_c, opKey, indexStart, indexEnd, rpcUrl); + emit OperatorCommitmentAdded(moduleId, operatorId, indexStart, indexEnd, rpcUrl); + + /// @dev update the opt-in delay off block and save state + _setOptInOutStateWithOptInDelay(opKey, optInOutState); + } + + /// @notice Delete the operator's commitment (keys kr) + /// @dev should be called by the operator manager address + /// @dev `opt-out` action + function delOperatorCommitment(uint256 cIdx) external whenNotPaused { + (uint256 opKey, OptInOutState memory optInOutState, OperatorOptInOutFlags memory flags) = + _checkOptInAndLoadStateByManager(msg.sender); + _checkOptOutDelayed(flags); + + KeyRange memory kr = _getOperatorCommitmentStorage(opKey, cIdx).keyRange; + _delOperatorCommitment(opKey, cIdx); + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + emit OperatorCommitmentDeleted(moduleId, operatorId, kr.indexStart, kr.indexEnd); + + /// @dev update the opt-out delay off block and save state + _setOptInOutStateWithOptOutDelay(opKey, optInOutState); + } + + /// @dev if the new kr just extends existing one - it's considered as an `opt-in` action, otherwise - `opt-out` action + function updateOperatorCommitment(uint256 cIdx, uint64 indexStart, uint64 indexEnd, string calldata rpcUrl) + external + whenNotPaused + { + (uint256 opKey, OptInOutState memory optInOutState, OperatorOptInOutFlags memory flags) = + _checkOptInAndLoadStateByManager(msg.sender); + + bool isExtend = true; + KeyRange memory kr = _getOperatorCommitmentStorage(opKey, cIdx).keyRange; + if (indexStart > kr.indexStart || indexEnd < kr.indexEnd) { + isExtend = false; + } + + if (isExtend) { + _checkOptInDelayed(flags); + } else { + _checkOptOutDelayed(flags); + } + + // delete the old commitment + _delOperatorCommitment(opKey, cIdx); + + // add the new commitment + LidoOperatorCache memory _c; + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + _loadLidoNodeOperator(_c, moduleId, operatorId); + _checkAndAddCommitment(_c, opKey, indexStart, indexEnd, rpcUrl); + emit OperatorCommitmentDeleted(moduleId, operatorId, kr.indexStart, kr.indexEnd); + emit OperatorCommitmentAdded(moduleId, operatorId, indexStart, indexEnd, rpcUrl); + + if (isExtend) { + /// @dev update the opt-in delay off block and save state + _setOptInOutStateWithOptInDelay(opKey, optInOutState); + } else { + /// @dev update the opt-out delay off block and save state + _setOptInOutStateWithOptOutDelay(opKey, optInOutState); + } + } + + function updateOperatorCommitmentExtraData(uint256 cIdx, string calldata rpcUrl) external whenNotPaused { + (uint256 opKey,,) = _checkOptInAndLoadStateByManager(msg.sender); + + KeyRange memory kr = _getOperatorCommitmentStorage(opKey, cIdx).keyRange; + _setOperatorCommitmentRPCUrl(opKey, cIdx, rpcUrl); + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + emit OperatorCommitmentAdded(moduleId, operatorId, kr.indexStart, kr.indexEnd, rpcUrl); + } + + function getOperatorManager(uint24 moduleId, uint64 operatorId) external view returns (address) { + uint256 opKey = __encOpKey(moduleId, operatorId); + return _getOperatorManager(opKey); + } + + /// @notice Update the operator's manager address + /// @dev should be called by the operator reward address + function updateOperatorManager(uint24 moduleId, uint64 operatorId, address newManager) external whenNotPaused { + if (newManager == address(0)) revert ZeroOperatorManagerAddress(); + + LidoOperatorCache memory _c; + /// @dev correctness of moduleId and operatorId are checked inside + _loadLidoNodeOperator(_c, moduleId, operatorId); + + // check if the caller is the operator's reward address + if (msg.sender != _c.rewardAddress) { + revert RewardAddressMismatch(); + } + + // check if the operator is active in Lido module + if (!_c.isActive) { + revert OperatorNotActive(); + } + + uint256 opKey = _getOpKeyById(moduleId, operatorId); + if (_getIsOperatorBlocked(opKey)) { + revert OperatorBlocked(); + } + + _checkOptInAndLoadState(opKey); + + /// @dev also checks if the proposed manager is already registered for different operator + _setOperatorManager(opKey, newManager); + emit OperatorManagerUpdated(moduleId, operatorId, newManager); + } + + function getOperator(uint24 moduleId, uint64 operatorId) + external + view + returns (address manager, bool isBlocked, bool isEnabled, OptInOutState memory optInOutState) + { + uint256 opKey = _getOpKeyById(moduleId, operatorId); + manager = _getOperatorManager(opKey); + isBlocked = _getIsOperatorBlocked(opKey); + optInOutState = _getOperatorOptInOutState(opKey); + + isEnabled = _isOperatorIsEnabledForPreconf(moduleId, operatorId, optInOutState); + } + + // + function getOperatorIsEnabledForPreconf(uint24 moduleId, uint64 operatorId) external view returns (bool) { + uint256 opKey = __encOpKey(moduleId, operatorId); + OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); + + return _isOperatorIsEnabledForPreconf(moduleId, operatorId, optInOutState); + } + + function getOperatorAllowedKeys(uint24 moduleId, uint64 operatorId) external view returns (uint64 allowedKeys) { + uint256 opKey = __encOpKey(moduleId, operatorId); + if (_getIsOperatorBlocked(opKey)) { + return 0; + } + + // check if the module has max keys limit + allowedKeys = getModuleOperatorMaxKeys(moduleId); + if (allowedKeys == 0) { + return 0; + } + // check if the operator is active in Lido module + LidoOperatorCache memory _c; + _loadLidoNodeOperator(_c, moduleId, operatorId); + if (!_c.isActive) { + return 0; + } + // min(operator totalAddedKeys, moduleMaxKeys) + if (allowedKeys > _c.totalKeys) { + allowedKeys = _c.totalKeys; + } + + OperatorOptInOutFlags memory flags = _calcOptInOutFlags(_getOperatorOptInOutState(opKey)); + if (flags.isOptedIn) { + (uint64 committedKeys,) = _loadCommitmentKeyRanges(opKey); + // check if the operator has already reached the max keys limit + if (committedKeys >= allowedKeys) { + return 0; + } + unchecked { + allowedKeys -= committedKeys; + } + } + } + + function getConfig() + external + view + returns ( + uint64 optInDelayBlocks, + uint64 optOutDelayBlocks, + uint64 defaultOperatorMaxKeys, + uint64 defaultBlockGasLimit + ) + { + return _getConfig(); + } + + function getModuleConfig(uint24 moduleId) + external + view + returns (bool isDisabled, uint64 operatorMaxKeys, uint64 blockGasLimit) + { + return _getModuleConfig(moduleId); + } + + function getModuleBlockGasLimit(uint24 moduleId) external view returns (uint64) { + (,,, uint64 defaultBlockGasLimit) = _getConfig(); + (bool isDisabled,, uint64 blockGasLimit) = _getModuleConfig(moduleId); + return isDisabled ? 0 : blockGasLimit == 0 ? defaultBlockGasLimit : blockGasLimit; + } + + function getModuleOperatorMaxKeys(uint24 moduleId) public view returns (uint64) { + (,, uint64 defaultOperatorMaxKeys,) = _getConfig(); + (bool isDisabled, uint64 operatorMaxKeys,) = _getModuleConfig(moduleId); + return isDisabled ? 0 : operatorMaxKeys == 0 ? defaultOperatorMaxKeys : operatorMaxKeys; + } + + function getContractVersion() external view returns (uint64) { + return _getInitializedVersion(); + } + + function unblockOperator(uint24 moduleId, uint64 operatorId) external onlyRole(COMMITTEE_ROLE) { + uint256 opKey = _getOpKeyById(moduleId, operatorId); + bool isBlocked = _getIsOperatorBlocked(opKey); + if (isBlocked) { + _setIsOperatorBlocked(opKey, false); + } + emit UnblockOperator(moduleId, operatorId); + } + + /// @notice update min opt-in and opt-out delay durations, + /// default operator's max keys for the module and block gas limit + function setConfig( + uint64 optInDelayBlocks, + uint64 optOutDelayBlocks, + uint64 defaultOperatorMaxKeys, + uint64 defaultBlockGasLimit + ) external onlyRole(COMMITTEE_ROLE) { + _updateConfig(optInDelayBlocks, optOutDelayBlocks, defaultOperatorMaxKeys, defaultBlockGasLimit); + } + + /// @notice Update Disable/enable state and operator's max keys for the module + function setModuleConfig(uint24 moduleId, bool isDisabled, uint64 operatorMaxKeys, uint64 blockGasLimit) + external + onlyRole(COMMITTEE_ROLE) + { + /// @dev check moduleId via staking router + LidoOperatorCache memory _c; + _loadLidoModuleData(_c, moduleId); + + _setModuleConfig(moduleId, isDisabled, operatorMaxKeys, blockGasLimit); + emit ModuleConfigUpdated(moduleId, isDisabled, operatorMaxKeys, blockGasLimit); + } + + function _updateConfig( + uint64 optInDelayBlocks, + uint64 optOutDelayBlocks, + uint64 defaultOperatorMaxKeys, + uint64 defaultBlockGasLimit + ) internal { + _setConfig(optInDelayBlocks, optOutDelayBlocks, defaultOperatorMaxKeys, defaultBlockGasLimit); + emit ConfigUpdated(optInDelayBlocks, optOutDelayBlocks, defaultOperatorMaxKeys, defaultBlockGasLimit); + } + + function _checkAndAddCommitment( + LidoOperatorCache memory _c, + uint256 opKey, + uint64 indexStart, + uint64 indexEnd, + string calldata rpcUrl + ) internal { + if (indexStart > indexEnd) { + revert KeyIndexMismatch(); + } + + if (indexEnd >= _c.totalKeys) { + revert KeyIndexOutOfRange(); + } + + (uint64 committedKeys, KeyRange[] memory ranges) = _loadCommitmentKeyRanges(opKey); + + if (ranges.length >= MAX_COMMITMENTS) { + revert CommitmentsLimitReached(); + } + + // check for overlapping with existing ranges + for (uint256 i = 0; i < ranges.length;) { + // condition for overlapping two ranges [s1,e1] and [s2,e2]: + // they overlap if s1 <= e2 and s2 <= e1. + if ((indexStart <= ranges[i].indexEnd) && (ranges[i].indexStart <= indexEnd)) { + revert KeyIndexOverlapExisting(); + } + + unchecked { + ++i; + } + } + + // new total committed keys + committedKeys += indexEnd - indexStart + 1; + _checkModuleParams(_c.moduleId, committedKeys); + + /// @dev no checks on rpcUrl + _addOperatorCommitment(opKey, indexStart, indexEnd, rpcUrl); + } + + function _loadCommitmentKeyRanges(uint256 opKey) + internal + view + returns (uint64 committedKeys, KeyRange[] memory ranges) + { + Commitment[] storage commitments = _getOperatorCommitmentsStorage(opKey); + uint256 commitmentsLen = commitments.length; + uint64 rangeLen; + + ranges = new KeyRange[](commitmentsLen); + + for (uint256 i = 0; i < commitmentsLen;) { + ranges[i] = commitments[i].keyRange; + unchecked { + rangeLen = ranges[i].indexEnd - ranges[i].indexStart + 1; + ++i; + } + committedKeys += rangeLen; + } + } + + function _checkOptInAndLoadStateByManager(address manager) + internal + view + returns (uint256 opKey, OptInOutState memory optInOutState, OperatorOptInOutFlags memory flags) + { + opKey = _getOpKeyByManager(manager); + (optInOutState, flags) = _checkOptInAndLoadState(opKey); + } + + function _checkOptInAndLoadState(uint256 opKey) + internal + view + returns (OptInOutState memory optInOutState, OperatorOptInOutFlags memory flags) + { + optInOutState = _getOperatorOptInOutState(opKey); + flags = _calcOptInOutFlags(optInOutState); + + if (!flags.isOptedIn) { + revert OperatorOptedOut(); + } + } + + function _isOperatorIsEnabledForPreconf(uint24 moduleId, uint64 operatorId, OptInOutState memory optInOutState) + internal + view + returns (bool) + { + OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); + (bool isModuleDisabled,,) = _getModuleConfig(moduleId); + + LidoOperatorCache memory _c; + _loadLidoNodeOperator(_c, moduleId, operatorId); + + // operator is enabled: + // - if it's s opted in + // - if module not disabled + // - if operator is active in Lido module + // - if the contract is not paused + return flags.isOptedIn && !isModuleDisabled && _c.isActive && !paused(); + } + + function _checkModuleParams(uint24 moduleId, uint64 committedKeys) internal view { + uint64 maxKeys = getModuleOperatorMaxKeys(moduleId); + if (maxKeys == 0) { + revert ModuleDisabled(); + } + if (committedKeys > maxKeys) { + revert KeyRangeExceedMaxKeys(); + } + } + + function _calcOptInOutFlags(OptInOutState memory optInOutState) + internal + view + returns (OperatorOptInOutFlags memory flags) + { + bool isOptedOut = optInOutState.optOutBlock > 0; // && blockNumber >= optInOutState.optOutBlock; + bool isOptedIn = optInOutState.optInBlock > 0 && !isOptedOut; + // any opt-in action is delayed after any opt-out action is made + bool isOptInDelayed = _isDelay(optInOutState.optOutBlock); + // any opt-out action is delayed after any opt-in action is made + bool isOptOutDelayed = _isDelay(optInOutState.optInBlock); + + return OperatorOptInOutFlags({ + isOptedIn: isOptedIn, + // isOptedOut: isOptedOut, + isOptInDelayed: isOptInDelayed, + isOptOutDelayed: isOptOutDelayed + }); + } + + /// CACHE + + /// @notice Get the staking router address from LidoLocator + function _getStakingRouter() internal view returns (IStakingRouter) { + return IStakingRouter(LIDO_LOCATOR.stakingRouter()); + } + + /// @notice Prepare the cache for the staking module and node operator + function _loadLidoNodeOperator(LidoOperatorCache memory _c, uint24 moduleId, uint64 operatorId) internal view { + _loadLidoModuleData(_c, moduleId); + _loadLidoNodeOperatorData(_c, operatorId); + } + + function _loadLidoModuleData(LidoOperatorCache memory _c, uint24 moduleId) internal view { + /// @dev module id validity check is done in the staking router + StakingModule memory module = _getStakingRouter().getStakingModule(moduleId); + + _c.moduleId = moduleId; + _c.moduleAddress = module.stakingModuleAddress; + } + + function _loadLidoNodeOperatorData(LidoOperatorCache memory _c, uint64 operatorId) internal view { + if (_c.moduleId == 0) { + revert InvalidModuleId(); + } + + /// @dev check if the operatorId is valid + uint64 totalOperatorsCount = uint64(IStakingModule(_c.moduleAddress).getNodeOperatorsCount()); + if (operatorId >= totalOperatorsCount) { + revert InvalidOperatorId(); + } + _c.operatorId = operatorId; + + /// @dev check for the CSModule type + bytes32 moduleType = IStakingModule(_c.moduleAddress).getType(); + if (moduleType == CS_MODULE_TYPE) { + ICSModule module = ICSModule(_c.moduleAddress); + _c.isActive = module.getNodeOperatorIsActive(operatorId); + CSMNodeOperator memory operator = module.getNodeOperator(operatorId); + _c.rewardAddress = operator.rewardAddress; + _c.totalKeys = operator.totalAddedKeys; + } else { + ICuratedModule module = ICuratedModule(_c.moduleAddress); + (_c.isActive,, _c.rewardAddress,,, _c.totalKeys) = module.getNodeOperator(operatorId, false); + } + } + + function _isDelay(uint64 offBlock) internal view returns (bool) { + return offBlock >= block.number; + } + + function _checkOptInDelayed(OperatorOptInOutFlags memory flags) internal pure { + if (flags.isOptInDelayed) { + revert OptInActionDelayed(); + } + } + + function _checkOptOutDelayed(OperatorOptInOutFlags memory flags) internal pure { + if (flags.isOptOutDelayed) { + revert OptOutActionDelayed(); + } + } + + function _setOptInOutStateWithOptOutDelay(uint256 opKey, OptInOutState memory state) internal { + (, uint64 optOutDelayBlocks,,) = _getConfig(); + state.optOutDelayOffBlock = uint64(block.number + optOutDelayBlocks); + _setOperatorOptInOutState(opKey, state); + } + + function _setOptInOutStateWithOptInDelay(uint256 opKey, OptInOutState memory state) internal { + (uint64 optInDelayBlocks,,,) = _getConfig(); + state.optInDelayOffBlock = uint64(block.number + optInDelayBlocks); + _setOperatorOptInOutState(opKey, state); + } +} diff --git a/src/CredibleCommitmentCurationProvider.sol b/src/CredibleCommitmentCurationProvider.sol deleted file mode 100644 index 854709a..0000000 --- a/src/CredibleCommitmentCurationProvider.sol +++ /dev/null @@ -1,536 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Lido -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.28; - -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import {AccessControlEnumerableUpgradeable} from - "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; -import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; - -import { - CCCPDataStorage as DS, - OperatorState, - OperatorOptInOutState, - OperatorKeysRangeState, - OperatorExtraData, - ModuleState, - Config -} from "./lib/CCCPDataStorage.sol"; - -import {ILidoLocator} from "./interfaces/ILidoLocator.sol"; -import {StakingModule, IStakingRouter} from "./interfaces/IStakingRouter.sol"; -import {IStakingModule} from "./interfaces/IStakingModule.sol"; -import {CSMNodeOperator, ICSModule} from "./interfaces/ICSModule.sol"; -import {ICuratedModule} from "./interfaces/ICuratedModule.sol"; - -contract CredibleCommitmentCurationProvider is - Initializable, - AccessControlEnumerableUpgradeable, - PausableUpgradeable -{ - using DS for uint256; - - struct LidoOperatorCache { - uint24 moduleId; - address moduleAddress; - uint64 operatorId; - address rewardAddress; - uint64 totalKeys; - bool isActive; - } - - struct OperatorOptInOutFlags { - bool isOptedIn; - bool isOptedOut; - bool optInAllowed; - bool optOutAllowed; - } - - bytes32 public constant COMMITTEE_ROLE = keccak256("COMMITTEE_ROLE"); - ILidoLocator public immutable LIDO_LOCATOR; - // hardcoded CSModule type, used for the operator's state retrieval - bytes32 internal immutable CS_MODULE_TYPE; - - event OptInSucceeded(uint256 indexed moduleId, uint256 indexed operatorId, address manager); - event OptOutRequested(uint256 indexed moduleId, uint256 indexed operatorId, bool isForced); - event KeyRangeUpdated( - uint256 indexed moduleId, uint256 indexed operatorId, uint256 keysRangeStart, uint256 keysRangeEnd - ); - event RPCUrlUpdated(uint256 indexed moduleId, uint256 indexed operatorId, string rpcURL); - event ModuleStateUpdated(uint256 indexed moduleId, bool isDisabled, uint256 operatorMaxValidators); - event ConfigUpdated( - uint256 optInMinDurationBlocks, - uint256 optOutDelayDurationBlocks, - uint256 defaultOperatorMaxValidators, - uint256 defaultBlockGasLimit - ); - event ResetForcedOptOut(uint256 indexed moduleId, uint256 indexed operatorId); - - error OperatorNotRegistered(); - error ManagerNotRegistered(); - error RewardAddressMismatch(); - error OperatorNotActive(); - error ModuleDisabled(); - error SameValue(); - error OperatorAlreadyRegistered(); - error OperatorOptInNotAllowed(); - error OperatorOptOutNotAllowed(); - error KeyIndexOutOfRange(); - error KeysRangeExceedMaxValidators(); - error KeyIndexMismatch(); - - error InvalidOperatorId(); - error InvalidModuleId(); - error ZeroCommitteeAddress(); - error ZeroOperatorManagerAddress(); - error ZeroLocatorAddress(); - error ZeroDefaultOperatorMaxValidators(); - error ZeroDefaultBlockGasLimit(); - - constructor(address lidoLocator, bytes32 csModuleType) { - if (lidoLocator == address(0)) revert ZeroLocatorAddress(); - LIDO_LOCATOR = ILidoLocator(lidoLocator); - - CS_MODULE_TYPE = csModuleType; - - _disableInitializers(); - } - - function initialize( - address committeeAddress, - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, - uint64 defaultBlockGasLimit - ) external initializer { - if (committeeAddress == address(0)) revert ZeroCommitteeAddress(); - - __Pausable_init(); - __AccessControlEnumerable_init(); - - _grantRole(DEFAULT_ADMIN_ROLE, committeeAddress); - _grantRole(COMMITTEE_ROLE, committeeAddress); - - _setConfig( - optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit - ); - } - - /// @notice Resume all operations after a pause - function unpause() external onlyRole(COMMITTEE_ROLE) { - _unpause(); - } - - /// @notice Pause all operations - function pause() external onlyRole(COMMITTEE_ROLE) { - _pause(); - } - - /// @notice Opt-in operator to the module - /// @dev prevent a repeated optin same operator with a different manager address, - /// or in case when operator changed reward address in the module, - /// the manager's address must first be changed to the new one - /// from operator's reward address (see `updateManagerAddress`) - function optIn( - uint24 moduleId, - uint64 operatorId, - address manager, - uint64 newKeyIndexRangeStart, - uint64 newKeyIndexRangeEnd, - string memory rpcURL - ) external whenNotPaused { - LidoOperatorCache memory _c; - - if (manager == address(0)) revert ZeroOperatorManagerAddress(); - - /// @dev correctness of moduleId and operatorId are checked inside - _loadLidoNodeOperator(_c, moduleId, operatorId); - - // check if the caller is the operator's reward address - if (msg.sender != _c.rewardAddress) { - revert RewardAddressMismatch(); - } - - // check if the operator is active in Lido module - if (!_c.isActive) { - revert OperatorNotActive(); - } - - uint256 opKey = DS.__encOpKey(moduleId, operatorId); - - // check if the operator is already has the state - OperatorOptInOutState memory optInOutState = opKey._getOperatorOptInOutState(); - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); - if (flags.isOptedIn) { - revert OperatorAlreadyRegistered(); - } else if (!flags.optInAllowed) { - revert OperatorOptInNotAllowed(); - } - - // save operator state - /// @dev also checks if the proposed manager is already registered for different operator - opKey._setOperatorManager(manager); - opKey._setOperatorOptInOutState( - OperatorOptInOutState({optInBlock: uint64(block.number), optOutBlock: 0, isOptOutForced: false}) - ); - emit OptInSucceeded(moduleId, operatorId, manager); - - _checkAndUpdateKeysRange(_c, opKey, newKeyIndexRangeStart, newKeyIndexRangeEnd); - - /// @dev no checks on rpcUrl, so it can be rewritten on repeated opt-in - opKey._setOperatorExtraData(OperatorExtraData({rpcURL: rpcURL})); - // emit RPCUrlUpdated(moduleId, operatorId, rpcURL); - } - - /// @notice Opt-out operator on behalf of the operator manager - /// @dev should be called by the operator manager address - function optOut() external whenNotPaused { - uint256 opKey = _getOpKeyByManager(msg.sender); - OperatorOptInOutState memory optInOutState = opKey._getOperatorOptInOutState(); - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); - if (!flags.isOptedIn) { - revert OperatorNotActive(); - } else if (!flags.optOutAllowed) { - revert OperatorOptOutNotAllowed(); - } - - optInOutState.optOutBlock = uint64(block.number); - opKey._setOperatorOptInOutState(optInOutState); - - (uint24 moduleId, uint64 operatorId) = opKey.__decOpKey(); - emit OptOutRequested(moduleId, operatorId, false); - } - - /// @notice Opt-out operator on behalf of the committee - function optOut(uint24 moduleId, uint64 operatorId) external onlyRole(COMMITTEE_ROLE) { - uint256 opKey = _getOpKeyById(moduleId, operatorId); - - OperatorOptInOutState memory optInOutState = opKey._getOperatorOptInOutState(); - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); - if (!flags.isOptedIn) { - revert OperatorNotActive(); - } - /// @dev ignore optOutAllowed, as the committee can force opt-out - - optInOutState.optOutBlock = uint64(block.number); - optInOutState.isOptOutForced = true; - opKey._setOperatorOptInOutState(optInOutState); - - emit OptOutRequested(moduleId, operatorId, true); - } - - function updateKeysRange(uint64 newKeyIndexRangeStart, uint64 newKeyIndexRangeEnd) external whenNotPaused { - uint256 opKey = _getOpKeyByManager(msg.sender); - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(opKey._getOperatorOptInOutState()); - if (!flags.isOptedIn) { - revert OperatorNotActive(); - } - - OperatorKeysRangeState memory keysRangeState = opKey._getOperatorKeysRangeState(); - if ( - newKeyIndexRangeStart > keysRangeState.indexStart || newKeyIndexRangeEnd < keysRangeState.indexEnd - || (newKeyIndexRangeStart == keysRangeState.indexStart && newKeyIndexRangeEnd == keysRangeState.indexEnd) - ) { - revert KeyIndexMismatch(); - } - LidoOperatorCache memory _c; - _loadLidoNodeOperator(_c, opKey); - _checkAndUpdateKeysRange(_c, opKey, newKeyIndexRangeStart, newKeyIndexRangeEnd); - } - - function updateManager(uint24 moduleId, uint64 operatorId, address newManager) external whenNotPaused { - if (newManager == address(0)) revert ZeroOperatorManagerAddress(); - - LidoOperatorCache memory _c; - /// @dev correctness of moduleId and operatorId are checked inside - _loadLidoNodeOperator(_c, moduleId, operatorId); - - // check if the caller is the operator's reward address - if (msg.sender != _c.rewardAddress) { - revert RewardAddressMismatch(); - } - - uint256 opKey = DS.__encOpKey(moduleId, operatorId); - /// @dev also checks if the proposed manager is already registered for different operator - opKey._setOperatorManager(newManager); - } - - function getOperator(address manager) - external - view - returns (uint24 moduleId, uint64 operatorId, bool isEnabled, OperatorState memory state) - { - uint256 opKey = _getOpKeyByManager(manager); - return _getOperator(opKey); - } - - function getOperator(uint24 _moduleId, uint64 _operatorId) - external - view - returns (uint24 moduleId, uint64 operatorId, bool isEnabled, OperatorState memory state) - { - uint256 opKey = _getOpKeyById(_moduleId, _operatorId); - return _getOperator(opKey); - } - - function getConfig() - external - view - returns ( - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, - uint64 defaultBlockGasLimit - ) - { - Config memory cfg = DS._getConfig(); - return ( - cfg.optInMinDurationBlocks, - cfg.optOutDelayDurationBlocks, - cfg.defaultOperatorMaxValidators, - cfg.defaultBlockGasLimit - ); - } - - function resetForcedOptOut(uint24 moduleId, uint64 operatorId) external onlyRole(COMMITTEE_ROLE) { - uint256 opKey = _getOpKeyById(moduleId, operatorId); - OperatorOptInOutState memory optInOutState = opKey._getOperatorOptInOutState(); - if (optInOutState.isOptOutForced) { - optInOutState.isOptOutForced = false; - opKey._setOperatorOptInOutState(optInOutState); - } - emit ResetForcedOptOut(moduleId, operatorId); - } - - /// @notice update min opt-in and opt-out delay durations, - /// default operator's max validators for the module and block gas limit - function setConfig( - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, - uint64 defaultBlockGasLimit - ) external onlyRole(COMMITTEE_ROLE) { - _setConfig( - optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit - ); - } - - /// @notice Update Disable/enable state and operator's max validators for the module - function setModuleState(uint24 moduleId, bool isDisabled, uint64 operatorMaxValidators) - external - onlyRole(COMMITTEE_ROLE) - { - /// @dev check moduleId via staking router - LidoOperatorCache memory _c; - _loadLidoModuleData(_c, moduleId); - - ModuleState memory state = DS._getModuleState(moduleId); - /// @dev operators in disabled modules are automatically considered as opted-out - state.isDisabled = isDisabled; - /// @dev zero value means use default config - state.maxValidators = operatorMaxValidators; - DS._setModuleState(moduleId, state); - - emit ModuleStateUpdated(moduleId, isDisabled, operatorMaxValidators); - } - - function _setConfig( - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, - uint64 defaultBlockGasLimit - ) internal { - if (defaultOperatorMaxValidators == 0) { - revert ZeroDefaultOperatorMaxValidators(); - } - if (defaultBlockGasLimit == 0) { - revert ZeroDefaultBlockGasLimit(); - } - DS._setConfig( - Config({ - optInMinDurationBlocks: optInMinDurationBlocks, - optOutDelayDurationBlocks: optOutDelayDurationBlocks, - defaultOperatorMaxValidators: defaultOperatorMaxValidators, - defaultBlockGasLimit: defaultBlockGasLimit - }) - ); - emit ConfigUpdated( - optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit - ); - } - - function _checkAndUpdateKeysRange( - LidoOperatorCache memory _c, - uint256 opKey, - uint64 newKeyIndexRangeStart, - uint64 newKeyIndexRangeEnd - ) internal { - _checkKeysRangeIsValid(_c.totalKeys, newKeyIndexRangeStart, newKeyIndexRangeEnd); - _checkModuleParams(_c.moduleId, newKeyIndexRangeStart, newKeyIndexRangeEnd); - - // save operator state - opKey._setOperatorKeysRangeState( - OperatorKeysRangeState({indexStart: newKeyIndexRangeStart, indexEnd: newKeyIndexRangeEnd}) - ); - - emit KeyRangeUpdated(_c.moduleId, _c.operatorId, newKeyIndexRangeStart, newKeyIndexRangeEnd); - } - - function _getOperator(uint256 opKey) - internal - view - returns ( - uint24 moduleId, - uint64 operatorId, - bool isEnabled, - // uint64 optInBlock, - // uint64 optOutBlock, - // bool isOptOutForced, // if the operator is forced to opt out by the committee - // uint64 keyIndexRangeStart, - // uint64 keyIndexRangeEnd, - // string memory rpcURL - OperatorState memory state - ) - { - LidoOperatorCache memory _c; - - _loadLidoNodeOperator(_c, opKey); - state = opKey._getOperatorState(); - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(state.optInOutState); - ModuleState memory moduleState = DS._getModuleState(_c.moduleId); - - // operator is enabled: - // - if it's s opted in - // - if module not disabled - // - if operator is active in Lido module - // - if the contract is not paused - isEnabled = flags.isOptedIn && !moduleState.isDisabled && _c.isActive && !paused(); - - return ( - _c.moduleId, - _c.operatorId, - isEnabled, - // state.optInOutState.optInBlock, - // state.optInOutState.optOutBlock, - // state.optInOutState.isOptOutForced, - // state.keysRangeState.indexStart, - // state.keysRangeState.indexEnd, - // state.extraData.rpcURL - state - ); - } - - function _checkModuleParams(uint24 moduleId, uint64 startIndex, uint64 endIndex) internal view { - Config memory cfg = DS._getConfig(); - ModuleState memory state = DS._getModuleState(moduleId); - if (state.isDisabled) { - revert ModuleDisabled(); - } - uint64 totalKeys = endIndex - startIndex + 1; - uint64 maxValidators = state.maxValidators == 0 ? cfg.defaultOperatorMaxValidators : state.maxValidators; - - if (totalKeys > maxValidators) { - revert KeysRangeExceedMaxValidators(); - } - } - - function _getOpKeyById(uint24 moduleId, uint64 operatorId) internal view returns (uint256 opKey) { - opKey = DS.__encOpKey(moduleId, operatorId); - if (opKey._getOperatorManager() == address(0)) { - revert OperatorNotRegistered(); - } - } - - function _getOpKeyByManager(address manager) internal view returns (uint256 opKey) { - opKey = DS._getManagerOpKey(manager); - if (opKey == 0) { - revert ManagerNotRegistered(); - } - } - - function _checkKeysRangeIsValid(uint64 totalKeys, uint64 startIndex, uint64 endIndex) internal pure { - if (startIndex > endIndex) { - revert KeyIndexMismatch(); - } - - if (endIndex >= totalKeys || startIndex >= totalKeys) { - revert KeyIndexOutOfRange(); - } - } - - function _calcOptInOutFlags(OperatorOptInOutState memory optInOutState) - internal - view - returns (OperatorOptInOutFlags memory flags) - { - uint64 blockNumber = uint64(block.number); - Config memory cfg = DS._getConfig(); - - // bool optOutInProgress = - // optInOutState.optOutBlock > 0 && optInOutState.optOutBlock + cfg.optOutDelayDurationBlocks >= blockNumber; - bool isOptedOut = - optInOutState.optOutBlock > 0 && optInOutState.optOutBlock + cfg.optOutDelayDurationBlocks < blockNumber; - bool isOptedIn = optInOutState.optInBlock > 0 && !isOptedOut; - bool optInAllowed = !isOptedIn && !optInOutState.isOptOutForced; - bool optOutAllowed = isOptedIn && optInOutState.optInBlock + cfg.optInMinDurationBlocks < blockNumber; - - return OperatorOptInOutFlags({ - isOptedIn: isOptedIn, - isOptedOut: isOptedOut, - optInAllowed: optInAllowed, - optOutAllowed: optOutAllowed - }); - } - - /// CACHE - - /// @notice Get the staking router address from LidoLocator - function _getStakingRouter() internal view returns (IStakingRouter) { - return IStakingRouter(LIDO_LOCATOR.stakingRouter()); - } - - /// @notice Prepare the cache for the staking module and node operator - function _loadLidoNodeOperator(LidoOperatorCache memory _c, uint256 opKey) internal view { - (uint24 moduleId, uint64 operatorId) = opKey.__decOpKey(); - _loadLidoNodeOperator(_c, moduleId, operatorId); - } - - function _loadLidoNodeOperator(LidoOperatorCache memory _c, uint24 moduleId, uint64 operatorId) internal view { - _loadLidoModuleData(_c, moduleId); - _loadLidoNodeOperatorData(_c, operatorId); - } - - function _loadLidoModuleData(LidoOperatorCache memory _c, uint24 moduleId) internal view { - /// @dev module id validity check is done in the staking router - StakingModule memory module = _getStakingRouter().getStakingModule(moduleId); - - _c.moduleId = moduleId; - _c.moduleAddress = module.stakingModuleAddress; - } - - function _loadLidoNodeOperatorData(LidoOperatorCache memory _c, uint64 operatorId) internal view { - if (_c.moduleId == 0) { - revert InvalidModuleId(); - } - - /// @dev check if the operatorId is valid - uint64 totalOperatorsCount = uint64(IStakingModule(_c.moduleAddress).getNodeOperatorsCount()); - if (operatorId >= totalOperatorsCount) { - revert InvalidOperatorId(); - } - - /// @dev check for the CSModule type - bytes32 moduleType = IStakingModule(_c.moduleAddress).getType(); - if (moduleType == CS_MODULE_TYPE) { - ICSModule module = ICSModule(_c.moduleAddress); - _c.isActive = module.getNodeOperatorIsActive(operatorId); - CSMNodeOperator memory operator = module.getNodeOperator(operatorId); - _c.rewardAddress = operator.rewardAddress; - _c.totalKeys = operator.totalAddedKeys; - } else { - ICuratedModule module = ICuratedModule(_c.moduleAddress); - (_c.isActive,, _c.rewardAddress,,, _c.totalKeys) = module.getNodeOperator(operatorId, false); - } - } -} diff --git a/src/interfaces/ICCR.sol b/src/interfaces/ICCR.sol new file mode 100644 index 0000000..a3e11f6 --- /dev/null +++ b/src/interfaces/ICCR.sol @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {ICCROperatorStatesStorage} from "./ICCROperatorStatesStorage.sol"; + +/** + * @title ICCR + * @notice Interface for CredibleCommitmentCurationProvider. + */ +interface ICCR is ICCROperatorStatesStorage { + function optIn( + uint24 moduleId, + uint64 operatorId, + address manager, + uint64 keyIndexStart, + uint64 keyIndexEnd, + string calldata rpcURL + ) external; + function optOut() external; + // function updateKeyRange(uint64 keyIndexStart, uint64 keyIndexEnd) external; + // function updateManager(uint24 moduleId, uint64 operatorId, address newManager) external; + + function getOperatorManager(uint24 moduleId, uint64 operatorId) external view returns (address); + function getOperatorDelegates(uint24 moduleId, uint64 operatorId) external view returns (Delegate[] memory); + function getOperatorCommitments(uint24 moduleId, uint64 operatorId) external view returns (Commitment[] memory); + + function getOperator(uint24 moduleId, uint64 operatorId) + external + view + returns (address manager, bool isBlocked, bool isEnabled, OptInOutState memory optInOutState); + function getModuleBlockGasLimit(uint24 moduleId) external view returns (uint64); + function getModuleOperatorMaxKeys(uint24 moduleId) external view returns (uint64); + function getOperatorIsEnabledForPreconf(uint24 moduleId, uint64 operatorId) external view returns (bool); + function getOperatorAllowedKeys(uint24 moduleId, uint64 operatorId) external view returns (uint64); +} diff --git a/src/interfaces/ICCRConfigStorage.sol b/src/interfaces/ICCRConfigStorage.sol new file mode 100644 index 0000000..88d39c3 --- /dev/null +++ b/src/interfaces/ICCRConfigStorage.sol @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +/** + * @title ICCRConfigStorage + * @notice Interface for interacting with the storage and control config params. + */ +interface ICCRConfigStorage { + /// @notice steaking module parameters + /// @dev override global default values, zero values means use default config + /// @param isDisabled is module disabled for pre-confs + /// operators in disabled modules are automatically considered as opted-out + /// @param operatorMaxKeys maximum number of keys per operator + /// @param blockGasLimit block gas limit + struct ModuleConfig { + bool isDisabled; + uint64 operatorMaxKeys; + uint64 blockGasLimit; + } + + /// @notice global config parameters + /// @param optInDelayBlocks minimum duration of the opt-in period in blocks + /// @param optOutDelayBlocks delay in blocks before the operator can opt-in again after opt-out + /// @param defaultOperatorMaxKeys default maximum number of keys per operator + /// @param defaultBlockGasLimit default block gas limit + struct Config { + uint64 optInDelayBlocks; + uint64 optOutDelayBlocks; + uint64 defaultOperatorMaxKeys; + uint64 defaultBlockGasLimit; + } + + struct ConfigStorage { + // module configs + mapping(uint256 => ModuleConfig) _modules; + // config + Config _config; + } + + error ZeroDefaultOperatorMaxKeys(); + error ZeroDefaultBlockGasLimit(); +} diff --git a/src/interfaces/ICCROperatorStatesStorage.sol b/src/interfaces/ICCROperatorStatesStorage.sol new file mode 100644 index 0000000..84325b6 --- /dev/null +++ b/src/interfaces/ICCROperatorStatesStorage.sol @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +/** + * @title IOperatorStatesStorage + * @notice Interface for interacting with the storage and control states of operators. + */ +interface ICCROperatorStatesStorage { + /// @notice operator optin/optout state + /// operator can be in several statuses: + // 1. new, not registered: optInBlock = 0, optOutBlock = 0 + // 2. registered: optInBlock > 0, optOutBlock = 0 + // 3. opt-out in progress: optInBlock > 0, optOutBlock > 0, optOutBlock + optOutDelayBlocksDelay >= block.number + // 4. forded opt-out in progress: optInBlock > 0, optOutBlock > 0, isBlocked = true, optOutBlock + optOutDelayBlocksDelay >= block.number + // 5. opt-out completed: optInBlock > 0, optOutBlock > 0, isBlocked = false, optOutBlock + optOutDelayBlocksDelay < block.number + // 6. forced opt-out completed: optInBlock > 0, optOutBlock > 0, isBlocked = true, optOutBlock + optOutDelayBlocksDelay < block.number + + // If isBlocked is set, optOutBlock has non-zero value of the block number when the operator was forced to opt out. + // If operator has "forced opt-out completed" status, it can't opt in again until the committee decides to allow it (clear isBlocked flag). + struct OptInOutState { + uint64 optInBlock; + uint64 optOutBlock; + uint64 optInDelayOffBlock; + uint64 optOutDelayOffBlock; + } + + struct KeyRange { + uint64 indexStart; + uint64 indexEnd; + } + + struct ExtraData { + string rpcURL; + } + + struct Delegate { + bytes key; + } + + struct Commitment { + bytes32 id; // reserved for future use + KeyRange keyRange; + ExtraData extraData; + } + + struct OperatorStateOld { + address manager; + KeyRange keyRange; + OptInOutState optInOutState; + ExtraData extraData; + } + + struct OperatorState { + bool isBlocked; // if the operator is blocked (i.e. forced to opt out by the committee) + address manager; + OptInOutState optInOutState; + Commitment[] commitments; + Delegate[] delegates; + } + + /** + * @notice Storage structure for operator states data. + * @dev + * @param _operators Mapping opKey (module id + operator id) to operator state + * @param _managers Mapping manager address to opKey + */ + struct OperatorsStatesStorage { + mapping(uint256 => OperatorState) _operators; + mapping(address => uint256) _managers; + } + + error ManagerBelongsToOtherOperator(); + error OperatorNotRegistered(); + error ManagerNotRegistered(); + error IndexOutOfRange(); +} diff --git a/src/interfaces/ICSModule.sol b/src/interfaces/ICSModule.sol index 7840775..26a27dd 100644 --- a/src/interfaces/ICSModule.sol +++ b/src/interfaces/ICSModule.sol @@ -10,8 +10,8 @@ struct CSMNodeOperator { uint32 totalWithdrawnKeys; uint32 totalDepositedKeys; uint32 totalVettedKeys; - uint32 stuckValidatorsCount; - uint32 depositableValidatorsCount; + uint32 stuckKeysCount; + uint32 depositableKeysCount; uint32 targetLimit; uint8 targetLimitMode; uint32 totalExitedKeys; diff --git a/src/interfaces/ICuratedModule.sol b/src/interfaces/ICuratedModule.sol index 95d7f19..0a7ae08 100644 --- a/src/interfaces/ICuratedModule.sol +++ b/src/interfaces/ICuratedModule.sol @@ -13,8 +13,8 @@ interface ICuratedModule is IStakingModule { bool active, string memory name, address rewardAddress, - uint64 totalVettedValidators, - uint64 totalExitedValidators, - uint64 totalAddedValidators + uint64 totalVettedKeys, + uint64 totalExitedKeys, + uint64 totalAddedKeys ); } diff --git a/src/interfaces/IStakingRouter.sol b/src/interfaces/IStakingRouter.sol index 42e7b09..9e29749 100644 --- a/src/interfaces/IStakingRouter.sol +++ b/src/interfaces/IStakingRouter.sol @@ -13,7 +13,7 @@ struct StakingModule { string name; uint64 lastDepositAt; uint256 lastDepositBlock; - uint256 exitedValidatorsCount; + uint256 exitedKeysCount; uint16 priorityExitShareThreshold; uint64 maxDepositsPerBlock; uint64 minDepositBlockDistance; diff --git a/src/lib/CCCPDataStorage.sol b/src/lib/CCCPDataStorage.sol deleted file mode 100644 index b505f2b..0000000 --- a/src/lib/CCCPDataStorage.sol +++ /dev/null @@ -1,171 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Lido -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.28; - -/// @notice operator optin/optout state -/// operator can be in several statuses: -// 1. new, not registered: optInBlock = 0, optOutBlock = 0 -// 2. registered: optInBlock > 0, optOutBlock = 0 -// 3. opt-out in progress: optInBlock > 0, optOutBlock > 0, optOutBlock + optOutDelayDurationBlocksDelay >= block.r -// 4. forded opt-out in progress: optInBlock > 0, optOutBlock > 0, isOptOutForced = true, optOutBlock + optOutDelayDurationBlocksDelay >= block.r -// 5. opt-out completed: optInBlock > 0, optOutBlock > 0, isOptOutForced = false, optOutBlock + optOutDelayDurationBlocksDelay < block.r -// 6. forced opt-out completed: optInBlock > 0, optOutBlock > 0, isOptOutForced = true, optOutBlock + optOutDelayDurationBlocksDelay < block.r - -// If isOptOutForced is set, optOutBlock has non-zero value of the block number when the operator was forced to opt out. -// If operator has "forced opt-out completed" status, it can't opt in again until the committee decides to allow it (clear isOptOutForced flag). -struct OperatorOptInOutState { - uint64 optInBlock; - uint64 optOutBlock; - bool isOptOutForced; // if the operator is forced to opt out by the committee -} - -struct OperatorKeysRangeState { - uint64 indexStart; - uint64 indexEnd; -} - -struct OperatorExtraData { - string rpcURL; -} - -struct OperatorState { - address manager; - OperatorKeysRangeState keysRangeState; - OperatorOptInOutState optInOutState; - OperatorExtraData extraData; -} - -struct ModuleState { - // is module disabled for pre-confs - bool isDisabled; - // hopefully, we won't need more than 2^64 validators - uint64 maxValidators; -} - -struct Config { - // minimum duration of the opt-in period in blocks - uint64 optInMinDurationBlocks; - // delay in blocks before the operator can opt-in again after opt-out - uint64 optOutDelayDurationBlocks; - uint64 defaultOperatorMaxValidators; //todo rename to per op - uint64 defaultBlockGasLimit; -} - -library CCCPDataStorage { - struct CCCPData { - // opKey (module id + operator id) => operator state - mapping(uint256 => OperatorState) _operators; - // manager address to opKey - mapping(address => uint256) _managers; - // modules state - mapping(uint256 => ModuleState) _modules; - // config - Config _config; - } - - // keccak256(abi.encode(uint256(keccak256("lido.cccp.CCCPData")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 internal constant CCCP_DATA_LOCATION = 0x250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd100; - - error ManagerBelongsToOtherOperator(); - - function _getStorage() private pure returns (CCCPData storage $) { - assembly { - $.slot := CCCP_DATA_LOCATION - } - } - - function _getOperatorStateStorage(uint256 opKey) private view returns (OperatorState storage) { - return _getStorage()._operators[opKey]; - } - - /// @notice get operator' full state - function _getOperatorState(uint256 opKey) internal view returns (OperatorState memory) { - return _getOperatorStateStorage(opKey); - } - - /// @notice get operator's opt-in/opt-out state - function _getOperatorOptInOutState(uint256 opKey) internal view returns (OperatorOptInOutState memory) { - return _getOperatorStateStorage(opKey).optInOutState; - } - - function _setOperatorOptInOutState(uint256 opKey, OperatorOptInOutState memory state) internal { - _getOperatorStateStorage(opKey).optInOutState = state; - } - - /// @notice get operator's keys range state - function _getOperatorKeysRangeState(uint256 opKey) internal view returns (OperatorKeysRangeState memory) { - return _getOperatorStateStorage(opKey).keysRangeState; - } - - function _setOperatorKeysRangeState(uint256 opKey, OperatorKeysRangeState memory state) internal { - _getOperatorStateStorage(opKey).keysRangeState = state; - } - - /// @notice get operator's extra data - function _getOperatorExtraData(uint256 opKey) internal view returns (OperatorExtraData memory) { - return _getOperatorStateStorage(opKey).extraData; - } - - function _setOperatorExtraData(uint256 opKey, OperatorExtraData memory data) internal { - _getOperatorStateStorage(opKey).extraData = data; - } - - /// @notice get manager address linked to the operator's reward address - function _getOperatorManager(uint256 opKey) internal view returns (address managerAddress) { - return _getOperatorStateStorage(opKey).manager; - } - - /// @dev safe manager address update - function _setOperatorManager(uint256 opKey, address manager) internal { - _checkManager(opKey, manager); - OperatorState storage $ = _getOperatorStateStorage(opKey); - - address oldManager = $.manager; - if (oldManager != address(0) && oldManager != manager) { - delete _getStorage()._managers[oldManager]; - } - $.manager = manager; - _getStorage()._managers[manager] = opKey; - } - - function _getManagerOpKey(address manager) internal view returns (uint256) { - return _getStorage()._managers[manager]; - } - - function _checkManager(uint256 opKey, address manager) internal view { - uint256 managerOpKey = _getManagerOpKey(manager); - // revert if the manager address is linked to the other operator - if (managerOpKey != 0 && managerOpKey != opKey) { - revert ManagerBelongsToOtherOperator(); - } - } - - function __encOpKey(uint24 moduleId, uint64 operatorId) internal pure returns (uint256) { - return uint256(moduleId) << 64 | operatorId; - } - - function __decOpKey(uint256 opKey) internal pure returns (uint24 moduleId, uint64 operatorId) { - return (uint24(opKey >> 64), uint64(opKey)); - } - - /// MODULES DATA - - function _getModuleState(uint24 moduleId) internal view returns (ModuleState memory) { - return _getStorage()._modules[moduleId]; - } - - function _setModuleState(uint24 moduleId, ModuleState memory state) internal { - _getStorage()._modules[moduleId] = state; - } - - /// CONFIG DATA - - function _getConfig() internal view returns (Config memory) { - return _getStorage()._config; - } - - function _setConfig(Config memory config) internal { - _getStorage()._config = config; - } -} diff --git a/src/lib/CCRConfigStorage.sol b/src/lib/CCRConfigStorage.sol new file mode 100644 index 0000000..d8c4741 --- /dev/null +++ b/src/lib/CCRConfigStorage.sol @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {ICCRConfigStorage} from "../interfaces/ICCRConfigStorage.sol"; + +abstract contract CCRConfigStorage is ICCRConfigStorage { + bytes32 private immutable STORAGE_SLOT_REF; + + constructor() { + STORAGE_SLOT_REF = keccak256( + abi.encode(uint256(keccak256(abi.encodePacked("lido.ccr.storage.ConfigStorage"))) - 1) + ) & ~bytes32(uint256(0xff)); + } + + function _setConfig( + uint64 optInDelayBlocks, + uint64 optOutDelayBlocks, + uint64 defaultOperatorMaxKeys, + uint64 defaultBlockGasLimit + ) internal { + if (defaultOperatorMaxKeys == 0) { + revert ZeroDefaultOperatorMaxKeys(); + } + if (defaultBlockGasLimit == 0) { + revert ZeroDefaultBlockGasLimit(); + } + _getConfigStorage()._config = Config({ + optInDelayBlocks: optInDelayBlocks, + optOutDelayBlocks: optOutDelayBlocks, + defaultOperatorMaxKeys: defaultOperatorMaxKeys, + defaultBlockGasLimit: defaultBlockGasLimit + }); + } + + function _setModuleConfig(uint24 moduleId, bool isDisabled, uint64 operatorMaxKeys, uint64 blockGasLimit) + internal + { + _getConfigStorage()._modules[moduleId] = + ModuleConfig({isDisabled: isDisabled, operatorMaxKeys: operatorMaxKeys, blockGasLimit: blockGasLimit}); + } + + function _getConfig() + internal + view + returns ( + uint64 optInDelayBlocks, + uint64 optOutDelayBlocks, + uint64 defaultOperatorMaxKeys, + uint64 defaultBlockGasLimit + ) + { + Config memory config = _getConfigStorage()._config; + return ( + config.optInDelayBlocks, + config.optOutDelayBlocks, + config.defaultOperatorMaxKeys, + config.defaultBlockGasLimit + ); + } + + function _getModuleConfig(uint24 moduleId) + internal + view + returns (bool isDisabled, uint64 operatorMaxKeys, uint64 blockGasLimit) + { + ModuleConfig memory moduleConfig = _getConfigStorage()._modules[moduleId]; + return (moduleConfig.isDisabled, moduleConfig.operatorMaxKeys, moduleConfig.blockGasLimit); + } + + /** + * @notice Accesses the storage slot for the config's data. + * @return $ A reference to the `ConfigStorage` struct. + * + * @dev This function uses inline assembly to access a predefined storage slot. + */ + function _getConfigStorage() private view returns (ConfigStorage storage $) { + bytes32 slot = STORAGE_SLOT_REF; + assembly { + $.slot := slot + } + } +} diff --git a/src/lib/CCROperatorStatesStorage.sol b/src/lib/CCROperatorStatesStorage.sol new file mode 100644 index 0000000..bcc2c74 --- /dev/null +++ b/src/lib/CCROperatorStatesStorage.sol @@ -0,0 +1,189 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {ICCROperatorStatesStorage} from "../interfaces/ICCROperatorStatesStorage.sol"; + +abstract contract CCROperatorStatesStorage is ICCROperatorStatesStorage, Initializable { + bytes32 private immutable STORAGE_SLOT_REF; + + constructor() { + STORAGE_SLOT_REF = keccak256( + abi.encode(uint256(keccak256(abi.encodePacked("lido.ccr.storage.OperatorStatesStorage"))) - 1) + ) & ~bytes32(uint256(0xff)); + } + + function __initializeOperatorStatesStorage() internal onlyInitializing {} + + function _addOperatorCommitment(uint256 opKey, uint64 indexStart, uint64 indexEnd, string memory rpcUrl) internal { + Commitment[] storage commitments = _getOperatorCommitmentsStorage(opKey); + commitments.push( + Commitment({id: bytes32(0), keyRange: KeyRange(indexStart, indexEnd), extraData: ExtraData(rpcUrl)}) + ); + } + + function _delOperatorCommitment(uint256 opKey, uint256 cIdx) internal { + Commitment[] storage commitments = _getOperatorCommitmentsStorage(opKey); + uint256 length = commitments.length; + if (cIdx >= length) { + revert IndexOutOfRange(); + } + + // del element in O(1), by replacing it with the last one + unchecked { + if (cIdx < length - 1) { + commitments[cIdx] = commitments[length - 1]; + } + } + commitments.pop(); + } + + function _delOperatorAllCommitments(uint256 opKey) internal { + if (_getOperatorCommitmentsStorage(opKey).length > 0) { + delete _getOperatorStateStorage(opKey).commitments; + } + } + + function _addOperatorDelegate(uint256 opKey, bytes memory key) internal { + Delegate[] storage delegates = _getOperatorDelegatesStorage(opKey); + delegates.push(Delegate({key: key})); + } + + function _delOperatorDelegate(uint256 opKey, uint256 dIdx) internal { + Delegate[] storage delegates = _getOperatorDelegatesStorage(opKey); + uint256 length = delegates.length; + if (dIdx >= length) { + revert IndexOutOfRange(); + } + + // del element in O(1), by replacing it with the last one + unchecked { + if (dIdx < length - 1) { + delegates[dIdx] = delegates[length - 1]; + } + } + delegates.pop(); + } + + function _setOperatorCommitmentRPCUrl(uint256 opKey, uint256 cIdx, string memory rpcUrl) internal { + _getOperatorCommitmentStorage(opKey, cIdx).extraData = ExtraData({rpcURL: rpcUrl}); + } + + /// @dev safe manager's address update + function _setOperatorManager(uint256 opKey, address manager) internal { + // _checkManagerFree(opKey, manager); + uint256 managerOpKey = _getManagerOpKey(manager); + // revert if the manager address is linked to the other operator + if (managerOpKey != 0 && managerOpKey != opKey) { + revert ManagerBelongsToOtherOperator(); + } + + OperatorState storage $ = _getOperatorStateStorage(opKey); + address oldManager = $.manager; + if (oldManager != address(0) && oldManager != manager) { + delete _getOperatorsStatesStorage()._managers[oldManager]; + } + $.manager = manager; + _getOperatorsStatesStorage()._managers[manager] = opKey; + } + + /// @notice get operator' full state + function _getOperatorState(uint256 opKey) internal view returns (OperatorState memory) { + return _getOperatorStateStorage(opKey); + } + + /// @notice get operator's opt-in/opt-out state + function _getOperatorOptInOutState(uint256 opKey) internal view returns (OptInOutState memory) { + return _getOperatorStateStorage(opKey).optInOutState; + } + + function _getOperatorOptInOutStateStorage(uint256 opKey) internal view returns (OptInOutState storage) { + return _getOperatorStateStorage(opKey).optInOutState; + } + + function _setOperatorOptInOutState(uint256 opKey, OptInOutState memory state) internal { + _getOperatorStateStorage(opKey).optInOutState = state; + } + + /// @notice get operator's extra data + function _getOperatorCommitmentsStorage(uint256 opKey) internal view returns (Commitment[] storage) { + return _getOperatorStateStorage(opKey).commitments; + } + + function _getOperatorCommitmentStorage(uint256 opKey, uint256 cIdx) internal view returns (Commitment storage) { + // return _getOperatorStateStorage(opKey).commitments[cIdx]; + return _getOperatorCommitmentsStorage(opKey)[cIdx]; + } + + function _getOperatorDelegatesStorage(uint256 opKey) internal view returns (Delegate[] storage) { + return _getOperatorStateStorage(opKey).delegates; + } + + function _getOperatorDelegateStorage(uint256 opKey, uint256 dIdx) internal view returns (Delegate storage) { + return _getOperatorDelegatesStorage(opKey)[dIdx]; + } + + // function _getOperatorCommitmentExtraData(uint256 opKey, uint cIdx) internal view returns (ExtraData memory) { + // Commitment[] storage commitments = _getOperatorStateStorage(opKey).commitments; + // return commitments[cIdx].extraData; + // } + + /// @notice get manager address linked to the operator's reward address + function _getOperatorManager(uint256 opKey) internal view returns (address) { + return _getOperatorStateStorage(opKey).manager; + } + + function _getIsOperatorBlocked(uint256 opKey) internal view returns (bool) { + return _getOperatorStateStorage(opKey).isBlocked; + } + + function _setIsOperatorBlocked(uint256 opKey, bool isBlocked) internal { + _getOperatorStateStorage(opKey).isBlocked = isBlocked; + } + + function _getManagerOpKey(address manager) internal view returns (uint256) { + return _getOperatorsStatesStorage()._managers[manager]; + } + + /// @dev reverts if the operator not registered + function _getOpKeyById(uint24 moduleId, uint64 operatorId) internal view returns (uint256 opKey) { + opKey = __encOpKey(moduleId, operatorId); + if (_getOperatorManager(opKey) == address(0)) { + revert OperatorNotRegistered(); + } + } + + function _getOpKeyByManager(address manager) internal view returns (uint256 opKey) { + opKey = _getManagerOpKey(manager); + if (opKey == 0) { + revert ManagerNotRegistered(); + } + } + + function __encOpKey(uint24 moduleId, uint64 operatorId) internal pure returns (uint256) { + return (uint256(moduleId) << 64) | operatorId; + } + + function __decOpKey(uint256 opKey) internal pure returns (uint24 moduleId, uint64 operatorId) { + return (uint24(opKey >> 64), uint64(opKey)); + } + + /** + * @notice Accesses the storage slot for the OperatorState's data. + * @return $ A reference to the `OperatorsStatesStorage` struct. + * + * @dev This function uses inline assembly to access a predefined storage slot. + */ + function _getOperatorsStatesStorage() private view returns (OperatorsStatesStorage storage $) { + bytes32 slot = STORAGE_SLOT_REF; + assembly { + $.slot := slot + } + } + + function _getOperatorStateStorage(uint256 opKey) private view returns (OperatorState storage) { + return _getOperatorsStatesStorage()._operators[opKey]; + } +} diff --git a/test/CCCP.t.sol b/test/CCCP.t.sol deleted file mode 100644 index a6c0452..0000000 --- a/test/CCCP.t.sol +++ /dev/null @@ -1,346 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.28; - -import {Test, console} from "forge-std/Test.sol"; -import {CCCPDataStorage as DS, ModuleState, Config} from "../src/lib/CCCPDataStorage.sol"; -import { - CredibleCommitmentCurationProvider as CCCP, OperatorState -} from "../src/CredibleCommitmentCurationProvider.sol"; -import {CCCPMock} from "./helpers/mocks/CCCPMock.sol"; -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - -import "./helpers/Fixtures.sol"; -import "./helpers/Utilities.sol"; -import {LidoLocatorMock} from "test/helpers/mocks/LidoLocatorMock.sol"; -import {StakingModuleMock} from "test/helpers/mocks/StakingModuleMock.sol"; -import {StakingRouterMock} from "test/helpers/mocks/StakingRouterMock.sol"; -import {CuratedModuleMock} from "test/helpers/mocks/CuratedModuleMock.sol"; -import {CSModuleMock} from "test/helpers/mocks/CSModuleMock.sol"; - -abstract contract CCCPFixtures is Test, Fixtures, Utilities { - LidoLocatorMock public locator; - StakingRouterMock public sr; - CuratedModuleMock public nor; - CSModuleMock public csm; - - // address internal admin; - address internal stranger1; - address internal stranger2; - address internal noCsm1; - address internal noCsm1Manager; - address internal noCurated1; - address internal noCurated1Manager; - address internal committee; - - // uint64 optInMinDurationBlocks = 100; - // uint64 optOutDelayDurationBlocks = 200; - // uint64 defaultOperatorMaxValidators = 100; - // uint64 defaultBlockGasLimit = 1000000; - - // function createNo(uint256 modId, address rewAddr) internal returns (uint256) { - // StakingModuleMock m = StakingModuleMock(sr.getStakingModule(modId).stakingModuleAddress); - // return createNo(csm, rewAddr, 1); - // } - - function createNo(StakingModuleMock m, address rewAddr, uint32 keysCount) internal returns (uint64) { - m.addNo(true, rewAddr, keysCount); - return uint64(m.getNodeOperatorsCount() - 1); - } - - function updateNoKeys(StakingModuleMock m, uint256 noId, uint32 keysCount) internal { - m.updNoKeys(noId, keysCount); - } - - function updateNoActive(StakingModuleMock m, uint256 noId, bool active) internal { - m.updNoActive(noId, active); - } -} - -contract CCCPCommon is CCCPFixtures { - function setUp() public virtual { - committee = nextAddress("COMMITTEE"); - stranger1 = nextAddress("STRANGER1"); - stranger2 = nextAddress("STRANGER2"); - - (locator, sr, nor, csm) = initLidoMock(); - } -} - -contract CCCPInitialize is CCCPCommon { - function test_constructor() public { - CCCPMock cccp = new CCCPMock({lidoLocator: address(locator), csModuleType: "csm-type"}); - assertEq(cccp.__test__getCSModuleType(), "csm-type"); - assertEq(address(cccp.LIDO_LOCATOR()), address(locator)); - } - - function test_constructor_RevertWhen_ZeroLocator() public { - vm.expectRevert(CCCP.ZeroLocatorAddress.selector); - new CCCPMock({lidoLocator: address(0), csModuleType: "csm-type"}); - } - - function test_constructor_RevertWhen_InitOnImpl() public { - CCCPMock cccp = new CCCPMock({lidoLocator: address(locator), csModuleType: "csm-type"}); - - vm.expectRevert(Initializable.InvalidInitialization.selector); - cccp.initialize({ - committeeAddress: committee, - optInMinDurationBlocks: 0, - optOutDelayDurationBlocks: 0, - defaultOperatorMaxValidators: 10, - defaultBlockGasLimit: 1000000 - }); - } - - function test_initialize() public { - CCCPMock cccp = new CCCPMock({lidoLocator: address(locator), csModuleType: "csm-type"}); - _enableInitializers(address(cccp)); - cccp.initialize({ - committeeAddress: committee, - optInMinDurationBlocks: 32, - optOutDelayDurationBlocks: 64, - defaultOperatorMaxValidators: 10, - defaultBlockGasLimit: 1000000 - }); - - ( - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, - uint64 defaultBlockGasLimit - ) = cccp.getConfig(); - - assertEq(optInMinDurationBlocks, 32); - assertEq(optOutDelayDurationBlocks, 64); - assertEq(defaultOperatorMaxValidators, 10); - assertEq(defaultBlockGasLimit, 1000000); - assertFalse(cccp.paused()); - } -} - -contract CCCPOptIn is CCCPCommon { - CCCPMock public cccp; - - uint64 public noCsm1Id; - uint64 public noCurated1Id; - - uint24 public constant norId = 1; - uint24 public constant csmId = 2; - - function setUp() public virtual override { - super.setUp(); - - noCsm1 = nextAddress("NO_CSM1"); - noCurated1 = nextAddress("NO_CURATED1"); - noCsm1Manager = nextAddress("NO_CSM1_MANAGER"); - noCurated1Manager = nextAddress("NO_CURATED1_MANAGER"); - - cccp = new CCCPMock(address(locator), "community-onchain-v1"); - _enableInitializers(address(cccp)); - cccp.initialize({ - committeeAddress: committee, - optInMinDurationBlocks: 0, - optOutDelayDurationBlocks: 0, - defaultOperatorMaxValidators: 10, - defaultBlockGasLimit: 1000000 - }); - - noCsm1Id = createNo(csm, noCsm1, 10); - noCurated1Id = createNo(nor, noCurated1, 10); - } - - function test_OptIn() public { - // opt in on behalf of noCsm1 - vm.broadcast(noCsm1); - vm.expectEmit(true, true, true, false, address(cccp)); - emit CCCP.OptInSucceeded(csmId, noCsm1Id, noCsm1Manager); - vm.expectEmit(true, true, true, false, address(cccp)); - emit CCCP.KeyRangeUpdated(csmId, noCsm1Id, 2, 4); - - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, - rpcURL: "" - }); - - (uint24 moduleId, uint64 operatorId, bool isEnabled, OperatorState memory state) = - cccp.getOperator(noCsm1Manager); - - assertEq(moduleId, csmId); - assertEq(operatorId, noCsm1Id); - assertEq(isEnabled, true); - - assertEq(state.keysRangeState.indexStart, 2); - assertEq(state.keysRangeState.indexEnd, 4); - assertEq(state.manager, noCsm1Manager); - assertEq(state.optInOutState.optInBlock, block.number); - assertEq(state.optInOutState.optOutBlock, 0); - assertEq(state.optInOutState.isOptOutForced, false); - assertEq(state.extraData.rpcURL, ""); - } - - function test_OptIn_RevertWhen_ZeroManagerAddress() public { - vm.expectRevert(CCCP.ZeroOperatorManagerAddress.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: address(0), - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_WrongRewardAddress() public { - vm.broadcast(stranger1); - vm.expectRevert(CCCP.RewardAddressMismatch.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_WrongModuleId() public { - vm.broadcast(noCsm1); - vm.expectRevert(IStakingRouter.StakingModuleUnregistered.selector); - cccp.optIn({ - moduleId: 999, - operatorId: noCsm1Id, - manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_LidoOperatorNotActive() public { - // set noCsm1 to inactive - updateNoActive(csm, noCsm1Id, false); - - vm.broadcast(noCsm1); - vm.expectRevert(CCCP.OperatorNotActive.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_OperatorAlreadyOptedIn() public { - // optin - vm.broadcast(noCsm1); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, - rpcURL: "" - }); - - // repeat optin - vm.broadcast(noCsm1); - vm.expectRevert(CCCP.OperatorAlreadyRegistered.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_OperatorForceOptedOut() public { - // optin - vm.broadcast(noCsm1); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, - rpcURL: "" - }); - - // force optout - vm.roll(block.number + 100); - vm.broadcast(committee); - cccp.optOut({moduleId: csmId, operatorId: noCsm1Id}); - - // repeat optin - vm.roll(block.number + 100); - vm.broadcast(noCsm1); - vm.expectRevert(CCCP.OperatorOptInNotAllowed.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_ManagerBelongsOtherOperator() public { - // optin - vm.broadcast(noCurated1); - cccp.optIn({ - moduleId: norId, - operatorId: noCurated1Id, - manager: noCurated1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, - rpcURL: "" - }); - - // optin with same manager - vm.broadcast(noCsm1); - vm.expectRevert(DS.ManagerBelongsToOtherOperator.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCurated1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_KeyIndexWrongOrder() public { - // optin - vm.broadcast(noCsm1); - vm.expectRevert(CCCP.KeyIndexMismatch.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - newKeyIndexRangeStart: 4, - newKeyIndexRangeEnd: 2, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_KeyIndexOutOfRange() public { - // optin - vm.broadcast(noCsm1); - vm.expectRevert(CCCP.KeyIndexOutOfRange.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 100, - rpcURL: "" - }); - } -} diff --git a/test/CCR.t.sol b/test/CCR.t.sol new file mode 100644 index 0000000..edb6b35 --- /dev/null +++ b/test/CCR.t.sol @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.28; + +import {CCRCommon} from "./helpers/CCRCommon.sol"; + +import {CCR} from "../src/CCR.sol"; +import {CCRMock} from "./helpers/mocks/CCRMock.sol"; +import {IStakingRouter} from "test/helpers/mocks/StakingRouterMock.sol"; +import {ICCROperatorStatesStorage} from "../src/interfaces/ICCROperatorStatesStorage.sol"; + +contract CCROptIn is CCRCommon { + CCR public ccr; + + uint64 public noCsm1Id; + uint64 public noCurated1Id; + + uint24 public constant norId = 1; + uint24 public constant csmId = 2; + + string public constant rpcUrl1 = "some-url-1"; + string public constant rpcUrl2 = "some-url-2"; + + uint32 public constant opKeysCount = 10; + + uint64 public constant optInDelayBlocks = 0; + uint64 public constant optOutDelayBlocks = 0; + uint64 public constant defaultOperatorMaxKeys = 100; + uint64 public constant defaultBlockGasLimit = 1000000; + + function setUp() public virtual override { + super.setUp(); + + noCsm1 = nextAddress("NO_CSM1"); + noCurated1 = nextAddress("NO_CURATED1"); + noCsm1Manager = nextAddress("NO_CSM1_MANAGER"); + noCurated1Manager = nextAddress("NO_CURATED1_MANAGER"); + + ccr = new CCRMock(address(locator), "community-onchain-v1"); + _enableInitializers(address(ccr)); + ccr.initialize({ + committeeAddress: committee, + optInDelayBlocks: optInDelayBlocks, + optOutDelayBlocks: optOutDelayBlocks, + defaultOperatorMaxKeys: defaultOperatorMaxKeys, + defaultBlockGasLimit: defaultBlockGasLimit + }); + + noCsm1Id = createNo(csm, noCsm1, opKeysCount); + noCurated1Id = createNo(nor, noCurated1, opKeysCount); + } + + function test_OptIn() public { + // opt in on behalf of noCsm1 + vm.prank(noCsm1); + vm.expectEmit(true, true, true, false, address(ccr)); + emit CCR.OperatorManagerUpdated(csmId, noCsm1Id, noCsm1Manager); + vm.expectEmit(true, true, true, false, address(ccr)); + emit CCR.OperatorCommitmentAdded(csmId, noCsm1Id, 2, 4, rpcUrl1); + vm.expectEmit(true, true, true, false, address(ccr)); + emit CCR.OptInSucceeded(csmId, noCsm1Id); + + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: rpcUrl1 + }); + + assertEq(ccr.getOperatorIsEnabledForPreconf(csmId, noCsm1Id), true); + assertEq(ccr.getOperatorManager(csmId, noCsm1Id), noCsm1Manager); + } + + function test_GetOperator() public { + // opt in on behalf of noCsm1 + vm.prank(noCsm1); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: rpcUrl1 + }); + + (address manager, bool isBlocked, bool isEnabled, CCR.OptInOutState memory optInOutState) = + ccr.getOperator(csmId, noCsm1Id); + + assertEq(manager, noCsm1Manager); + assertEq(isBlocked, false); + assertEq(isEnabled, true); + assertEq(optInOutState.optInBlock, block.number); + assertEq(optInOutState.optOutBlock, 0); + + CCR.Commitment[] memory commitments = ccr.getOperatorCommitments(csmId, noCsm1Id); + + assertEq(commitments.length, 1); + assertEq(commitments[0].keyRange.indexStart, 2); + assertEq(commitments[0].keyRange.indexEnd, 4); + assertEq(commitments[0].extraData.rpcURL, rpcUrl1); + } + + function test_OptIn_RevertWhen_ZeroManagerAddress() public { + vm.expectRevert(CCR.ZeroOperatorManagerAddress.selector); + ccr.optIn({moduleId: csmId, operatorId: noCsm1Id, manager: address(0), indexStart: 2, indexEnd: 4, rpcURL: ""}); + } + + function test_OptIn_RevertWhen_WrongRewardAddress() public { + vm.prank(stranger1); + vm.expectRevert(CCR.RewardAddressMismatch.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + } + + function test_OptIn_RevertWhen_WrongModuleId() public { + vm.prank(noCsm1); + vm.expectRevert(IStakingRouter.StakingModuleUnregistered.selector); + ccr.optIn({moduleId: 999, operatorId: noCsm1Id, manager: noCsm1Manager, indexStart: 2, indexEnd: 4, rpcURL: ""}); + } + + function test_OptIn_RevertWhen_LidoOperatorNotActive() public { + // set noCsm1 to inactive + updateNoActive(csm, noCsm1Id, false); + + vm.prank(noCsm1); + vm.expectRevert(CCR.OperatorNotActive.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + } + + function test_OptIn_RevertWhen_OperatorAlreadyOptedIn() public { + // optin + vm.prank(noCsm1); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + + // repeat optin + vm.prank(noCsm1); + vm.expectRevert(CCR.OperatorOptedIn.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + } + + function test_OptIn_RevertWhen_OperatorForceOptedOut() public { + // optin + vm.prank(noCsm1); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + + // force optout + vm.roll(block.number + 100); + vm.prank(committee); + ccr.optOut({moduleId: csmId, operatorId: noCsm1Id}); + + // repeat optin + vm.roll(block.number + 100); + vm.prank(noCsm1); + vm.expectRevert(CCR.OperatorBlocked.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + } + + function test_OptIn_RevertWhen_ManagerBelongsOtherOperator() public { + // optin + vm.prank(noCurated1); + ccr.optIn({ + moduleId: norId, + operatorId: noCurated1Id, + manager: noCurated1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + + // optin with same manager + vm.prank(noCsm1); + vm.expectRevert(ICCROperatorStatesStorage.ManagerBelongsToOtherOperator.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCurated1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + } + + function test_OptIn_RevertWhen_KeyIndexWrongOrder() public { + // optin + vm.prank(noCsm1); + vm.expectRevert(CCR.KeyIndexMismatch.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 4, + indexEnd: 2, + rpcURL: "" + }); + } + + function test_OptIn_RevertWhen_KeyIndexOutOfRange() public { + // optin + vm.prank(noCsm1); + vm.expectRevert(CCR.KeyIndexOutOfRange.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 100, + rpcURL: "" + }); + } + + function test_getOperatorIsEnabledForPreconf() public { + // wrong op id + // assertFalse(ccr.getOperatorIsEnabledForPreconf(csmId, 999)); + + // not yet opted in + assertFalse(ccr.getOperatorIsEnabledForPreconf(csmId, noCsm1Id)); + // opt in on behalf of noCsm1 + vm.prank(noCsm1); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: rpcUrl1 + }); + // opted in + assertTrue(ccr.getOperatorIsEnabledForPreconf(csmId, noCsm1Id)); + } + + function test_GetOperatorAllowedKeys() public { + vm.prank(committee); + ccr.setModuleConfig(csmId, true, 0, 0); + // 0 for disabled module + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), 0); + + vm.prank(committee); + ccr.setModuleConfig(csmId, false, 0, 0); + + // set noCsm1 to inactive + updateNoActive(csm, noCsm1Id, false); + // 0 for inactive operator + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), 0); + updateNoActive(csm, noCsm1Id, true); + + /// operator NOT yet opted in + /// + vm.prank(committee); + ccr.setConfig(0, 0, opKeysCount - 1, defaultBlockGasLimit); + + // operatorMaxKeys when operatorMaxKeys < operatorTotalAddedKeys + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), opKeysCount - 1); + + vm.prank(committee); + ccr.setConfig(0, 99, defaultOperatorMaxKeys, defaultBlockGasLimit); + + // operatorTotalAddedKeys when operatorMaxKeys > operatorTotalAddedKeys + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), opKeysCount); + + // opt in on behalf of noCsm1 with 3 keys + vm.prank(noCsm1); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: rpcUrl1 + }); + + // operatorMaxKeys > operatorTotalAddedKeys + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), opKeysCount - 3); + + // voluntary opt out + vm.roll(block.number + 100); + vm.prank(noCsm1Manager); + ccr.optOut(); + + vm.roll(block.number + 100); + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), opKeysCount); + + // opt in on behalf of noCsm1 with 9 keys + vm.prank(noCsm1); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 0, + indexEnd: 8, + rpcURL: rpcUrl1 + }); + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), opKeysCount - 9); + + // reduce max operator keys in module config to 5 + vm.prank(committee); + ccr.setModuleConfig(csmId, false, 5, 0); + // operator opted in totalKeys > operatorMaxKeys + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), 0); + + // force optout + vm.roll(block.number + 100); + vm.prank(committee); + ccr.optOut({moduleId: csmId, operatorId: noCsm1Id}); + + // optOut finished, but forced optout + vm.roll(block.number + 100); + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), 0); + } +} diff --git a/test/CCRConfig.t.sol b/test/CCRConfig.t.sol new file mode 100644 index 0000000..0b1335e --- /dev/null +++ b/test/CCRConfig.t.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.28; + +import {CCRCommon} from "./helpers/CCRCommon.sol"; + +import {CCR} from "../src/CCR.sol"; +import {IStakingRouter} from "test/helpers/mocks/StakingRouterMock.sol"; +import {ICCRConfigStorage} from "../src/interfaces/ICCRConfigStorage.sol"; + +contract CCRConfig is CCRCommon { + CCR public ccr; + uint24 public moduleId = 1; + uint64 public maxKeys = 1111; + uint64 public blockGasLimit = 1111111; + + uint64 constant optInDelayBlocks = 32; + uint64 constant optOutDelayBlocks = 64; + uint64 constant defaultOperatorMaxKeys = 100; + uint64 constant defaultBlockGasLimit = 1000000; + + function setUp() public virtual override { + super.setUp(); + + ccr = new CCR(address(locator), "community-onchain-v1"); + _enableInitializers(address(ccr)); + ccr.initialize({ + committeeAddress: committee, + optInDelayBlocks: optInDelayBlocks, + optOutDelayBlocks: optOutDelayBlocks, + defaultOperatorMaxKeys: defaultOperatorMaxKeys, + defaultBlockGasLimit: defaultBlockGasLimit + }); + } + + function test_GetInitialConfig() public view { + ( + uint64 newoptInDelayBlocks, + uint64 newoptOutDelayBlocks, + uint64 newDefaultOperatorMaxKeys, + uint64 newDefaultBlockGasLimit + ) = ccr.getConfig(); + + assertEq(newoptInDelayBlocks, optInDelayBlocks); + assertEq(newoptOutDelayBlocks, optOutDelayBlocks); + assertEq(newDefaultOperatorMaxKeys, defaultOperatorMaxKeys); + assertEq(newDefaultBlockGasLimit, defaultBlockGasLimit); + } + + function test_SetConfig() public { + vm.prank(committee); + ccr.setConfig(10, 20, 30, 40); + ( + uint64 newoptInDelayBlocks, + uint64 newoptOutDelayBlocks, + uint64 newDefaultOperatorMaxKeys, + uint64 newDefaultBlockGasLimit + ) = ccr.getConfig(); + + assertEq(newoptInDelayBlocks, 10); + assertEq(newoptOutDelayBlocks, 20); + assertEq(newDefaultOperatorMaxKeys, 30); + assertEq(newDefaultBlockGasLimit, 40); + } + + function test_SetConfig_RevertWhen_CallerNotCommittee() public { + bytes32 role = ccr.COMMITTEE_ROLE(); + + vm.prank(stranger1); + expectRoleRevert(stranger1, role); + ccr.setConfig(optInDelayBlocks, optOutDelayBlocks, defaultOperatorMaxKeys, defaultBlockGasLimit); + } + + function test_SetConfig_RevertWhen_ZeroDefaultOperatorMaxKeys() public { + vm.prank(committee); + vm.expectRevert(ICCRConfigStorage.ZeroDefaultOperatorMaxKeys.selector); + ccr.setConfig(optInDelayBlocks, optOutDelayBlocks, 0, defaultBlockGasLimit); + } + + function test_SetConfig_RevertWhen_ZeroDefaultBlockGasLimit() public { + vm.prank(committee); + vm.expectRevert(ICCRConfigStorage.ZeroDefaultBlockGasLimit.selector); + ccr.setConfig(optInDelayBlocks, optOutDelayBlocks, defaultOperatorMaxKeys, 0); + } + + function test_SetModuleConfig() public { + vm.prank(committee); + ccr.setModuleConfig(moduleId, true, maxKeys, blockGasLimit); + (bool newIsDisabled, uint64 newMaxKeys, uint64 newblockGasLimit) = ccr.getModuleConfig(moduleId); + + assertEq(newIsDisabled, true); + assertEq(newMaxKeys, maxKeys); + assertEq(newblockGasLimit, blockGasLimit); + } + + function test_SetModuleConfig_RevertWhen_CallerNotCommittee() public { + bytes32 role = ccr.COMMITTEE_ROLE(); + + vm.prank(stranger1); + expectRoleRevert(stranger1, role); + ccr.setModuleConfig(moduleId, true, maxKeys, blockGasLimit); + } + + function test_SetModuleConfig_RevertWhen_WrongModuleId() public { + vm.prank(committee); + vm.expectRevert(IStakingRouter.StakingModuleUnregistered.selector); + ccr.setModuleConfig(999, true, maxKeys, blockGasLimit); + } + + function test_ModuleConfig_OverrideBlockGasLimit() public { + // module config not yet set + assertEq(ccr.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); + + vm.prank(committee); + ccr.setModuleConfig(moduleId, false, 0, blockGasLimit); + assertEq(ccr.getModuleBlockGasLimit(moduleId), blockGasLimit); + + // set block gas limit to 0 + vm.prank(committee); + ccr.setModuleConfig(moduleId, false, 0, 0); + assertEq(ccr.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); + } + + function test_ModuleConfig_ZeroBlockGasLimitWhenModuleDisabled() public { + // module config not yet set + assertEq(ccr.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); + + // disable module + vm.prank(committee); + ccr.setModuleConfig(moduleId, true, 0, 0); + assertEq(ccr.getModuleBlockGasLimit(moduleId), 0); + + // enable module + vm.prank(committee); + ccr.setModuleConfig(moduleId, false, 0, 0); + assertEq(ccr.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); + } + + function test_ModuleConfig_OverrideOperatorMaxKeys() public { + // module config not yet set + assertEq(ccr.getModuleOperatorMaxKeys(moduleId), defaultOperatorMaxKeys); + + vm.prank(committee); + ccr.setModuleConfig(moduleId, false, maxKeys, 0); + assertEq(ccr.getModuleOperatorMaxKeys(moduleId), maxKeys); + + // set block gas limit to 0 + vm.prank(committee); + ccr.setModuleConfig(moduleId, false, 0, 0); + assertEq(ccr.getModuleOperatorMaxKeys(moduleId), defaultOperatorMaxKeys); + } + + function test_ModuleConfig_ZeroOperatorMaxKeysWhenModuleDisabled() public { + // module config not yet set + assertEq(ccr.getModuleOperatorMaxKeys(moduleId), defaultOperatorMaxKeys); + + // disable module + vm.prank(committee); + ccr.setModuleConfig(moduleId, true, 0, 0); + assertEq(ccr.getModuleOperatorMaxKeys(moduleId), 0); + + // enable module + vm.prank(committee); + ccr.setModuleConfig(moduleId, false, 0, 0); + assertEq(ccr.getModuleOperatorMaxKeys(moduleId), defaultOperatorMaxKeys); + } +} diff --git a/test/CCRInit.t.sol b/test/CCRInit.t.sol new file mode 100644 index 0000000..db48d3c --- /dev/null +++ b/test/CCRInit.t.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.28; + +import {CCR} from "../src/CCR.sol"; +import {CCRMock} from "./helpers/mocks/CCRMock.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {CCRCommon} from "./helpers/CCRCommon.sol"; + +contract CCRInitialize is CCRCommon { + function test_constructor() public { + CCRMock ccr = new CCRMock({lidoLocator: address(locator), csModuleType: "csm-type"}); + assertEq(ccr.__test__getCSModuleType(), "csm-type"); + assertEq(address(ccr.LIDO_LOCATOR()), address(locator)); + assertEq(ccr.getContractVersion(), type(uint64).max); + } + + function test_constructor_RevertWhen_ZeroLocator() public { + vm.expectRevert(CCR.ZeroLocatorAddress.selector); + new CCR({lidoLocator: address(0), csModuleType: "csm-type"}); + } + + function test_constructor_RevertWhen_InitOnImpl() public { + CCR ccr = new CCR({lidoLocator: address(locator), csModuleType: "csm-type"}); + + vm.expectRevert(Initializable.InvalidInitialization.selector); + ccr.initialize({ + committeeAddress: committee, + optInDelayBlocks: 0, + optOutDelayBlocks: 0, + defaultOperatorMaxKeys: 10, + defaultBlockGasLimit: 1000000 + }); + } + + function test_initialize() public { + CCR ccr = new CCR({lidoLocator: address(locator), csModuleType: "csm-type"}); + _enableInitializers(address(ccr)); + ccr.initialize({ + committeeAddress: committee, + optInDelayBlocks: 32, + optOutDelayBlocks: 64, + defaultOperatorMaxKeys: 10, + defaultBlockGasLimit: 1000000 + }); + + (uint64 optInDelayBlocks, uint64 optOutDelayBlocks, uint64 defaultOperatorMaxKeys, uint64 defaultBlockGasLimit) + = ccr.getConfig(); + + assertEq(optInDelayBlocks, 32); + assertEq(optOutDelayBlocks, 64); + assertEq(defaultOperatorMaxKeys, 10); + assertEq(defaultBlockGasLimit, 1000000); + assertEq(ccr.getContractVersion(), 1); + assertFalse(ccr.paused()); + } +} diff --git a/test/DataStorage.t.sol b/test/DataStorage.t.sol deleted file mode 100644 index 1e11244..0000000 --- a/test/DataStorage.t.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.28; - -import {Test, console} from "forge-std/Test.sol"; -import {CCCPDataStorage as DS, ModuleState, Config} from "../src/lib/CCCPDataStorage.sol"; - -contract ModulesDataStorageTest is Test { - function setUp() public { - // counter.setNumber(0); - } - - function test_StorageLocationConstant() public pure { - bytes32 location = keccak256(abi.encode(uint256(keccak256("lido.cccp.CCCPData")) - 1)) & ~bytes32(uint256(0xff)); - - assertEq(DS.CCCP_DATA_LOCATION, location); - } - - function test_ModuleState() public { - uint24 moduleId = 111; - uint64 maxValidators = 1111; - ModuleState memory state = ModuleState({isDisabled: true, maxValidators: maxValidators}); - - DS._setModuleState(moduleId, state); - ModuleState memory newState = DS._getModuleState(moduleId); - - assertEq(newState.maxValidators, maxValidators); - assertEq(newState.isDisabled, true); - } - - function test_OptInOutConfig() public { - uint64 optInMinDurationBlocks = 123; - uint64 optOutDelayDurationBlocks = 234; - uint64 defaultOperatorMaxValidators = 100; - uint64 defaultBlockGasLimit = 1000000; - Config memory cfg = Config( - optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit - ); - - DS._setConfig(cfg); - Config memory newCfg = DS._getConfig(); - - assertEq(newCfg.optInMinDurationBlocks, optInMinDurationBlocks); - assertEq(newCfg.optOutDelayDurationBlocks, optOutDelayDurationBlocks); - assertEq(newCfg.defaultOperatorMaxValidators, defaultOperatorMaxValidators); - assertEq(newCfg.defaultBlockGasLimit, defaultBlockGasLimit); - } -} diff --git a/test/OssifiableProxy.t.sol b/test/OssifiableProxy.t.sol new file mode 100644 index 0000000..f119a7d --- /dev/null +++ b/test/OssifiableProxy.t.sol @@ -0,0 +1,160 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.28; + +import {Test} from "forge-std/Test.sol"; +import {OssifiableProxy} from "../src/lib/proxy/OssifiableProxy.sol"; +import {Utilities} from "./helpers/Utilities.sol"; +import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; + +contract InitializableImplementationStub { + uint256 public version; + + event Initialized(uint256 version); + event FallbackIsFired(); + + function initialize(uint256 _version) public { + version = _version; + emit Initialized(_version); + } + + fallback() external payable { + emit FallbackIsFired(); + } +} + +contract OssifiableProxyTest is Test, Utilities { + InitializableImplementationStub currentImpl; + InitializableImplementationStub nextImpl; + OssifiableProxy proxy; + address deployer; + address admin; + address stranger; + + function setUp() public { + deployer = address(this); + admin = nextAddress("admin"); + stranger = nextAddress("stranger"); + + currentImpl = new InitializableImplementationStub(); + nextImpl = new InitializableImplementationStub(); + + proxy = new OssifiableProxy(address(currentImpl), admin, "0x"); + } + + function test_constructor_revertWhenZeroAdmin() public { + vm.expectRevert(abi.encodeWithSelector(ERC1967Utils.ERC1967InvalidAdmin.selector, address(0))); + new OssifiableProxy(address(currentImpl), address(0), "0x"); + } + + function test_getAdmin() public view { + assertEq(proxy.proxy__getAdmin(), admin); + } + + function test_getImplementation() public view { + assertEq(proxy.proxy__getImplementation(), address(currentImpl)); + } + + function test_getIsOssified() public view { + assertFalse(proxy.proxy__getIsOssified()); + } + + function test_ossify_RevertWhenNotAdmin() public { + vm.prank(stranger); + vm.expectRevert(OssifiableProxy.NotAdmin.selector); + proxy.proxy__ossify(); + } + + function test_ossify() public { + vm.prank(admin); + proxy.proxy__ossify(); + assertTrue(proxy.proxy__getIsOssified()); + } + + function test_ossify_RevertWhenOssified() public { + vm.prank(admin); + proxy.proxy__ossify(); + + vm.expectRevert(OssifiableProxy.ProxyIsOssified.selector); + proxy.proxy__ossify(); + } + + function test_changeAdmin_RevertWhenNotAdmin() public { + vm.prank(stranger); + vm.expectRevert(OssifiableProxy.NotAdmin.selector); + proxy.proxy__changeAdmin(stranger); + } + + function test_changeAdmin_RevertWhenOssified() public { + vm.prank(admin); + proxy.proxy__ossify(); + assertTrue(proxy.proxy__getIsOssified()); + + vm.expectRevert(OssifiableProxy.ProxyIsOssified.selector); + proxy.proxy__changeAdmin(stranger); + } + + function test_changeAdmin() public { + vm.prank(admin); + proxy.proxy__changeAdmin(stranger); + assertEq(proxy.proxy__getAdmin(), stranger); + } + + function test_upgradeTo_RevertWhenNotAdmin() public { + vm.prank(stranger); + vm.expectRevert(OssifiableProxy.NotAdmin.selector); + proxy.proxy__upgradeTo(address(nextImpl)); + } + + function test_upgradeTo_RevertWhenOssified() public { + vm.prank(admin); + proxy.proxy__ossify(); + assertTrue(proxy.proxy__getIsOssified()); + + vm.expectRevert(OssifiableProxy.ProxyIsOssified.selector); + proxy.proxy__upgradeTo(address(nextImpl)); + } + + function test_upgradeTo() public { + vm.prank(admin); + proxy.proxy__upgradeTo(address(nextImpl)); + assertEq(proxy.proxy__getImplementation(), address(nextImpl)); + } + + function test_upgradeToAndCall_RevertWhenNotAdmin() public { + vm.prank(stranger); + vm.expectRevert(OssifiableProxy.NotAdmin.selector); + proxy.proxy__upgradeToAndCall(address(nextImpl), abi.encodeWithSelector(nextImpl.initialize.selector, 1)); + } + + function test_upgradeToAndCall_RevertWhenOssified() public { + vm.prank(admin); + proxy.proxy__ossify(); + assertTrue(proxy.proxy__getIsOssified()); + + vm.expectRevert(OssifiableProxy.ProxyIsOssified.selector); + proxy.proxy__upgradeToAndCall(address(nextImpl), abi.encodeWithSelector(nextImpl.initialize.selector, 1)); + } + + function test_upgradeToAndCall() public { + vm.prank(admin); + proxy.proxy__upgradeToAndCall(address(nextImpl), abi.encodeWithSelector(nextImpl.initialize.selector, 1)); + assertEq(proxy.proxy__getImplementation(), address(nextImpl)); + assertEq(InitializableImplementationStub(payable(address(proxy))).version(), 1); + } + + function test_receive() public { + vm.deal(admin, 2 ether); + vm.prank(admin); + vm.expectEmit(true, true, true, true, address(proxy)); + emit InitializableImplementationStub.FallbackIsFired(); + payable(address(proxy)).call{value: 1 ether}(""); + assertEq(address(proxy).balance, 1 ether); + } + + function test_fallback() public { + vm.prank(admin); + uint256 version = InitializableImplementationStub(payable(address(proxy))).version(); + assertEq(version, 0); + } +} diff --git a/test/fork/deployment/PostDeployment.t.sol b/test/fork/deployment/PostDeployment.t.sol new file mode 100644 index 0000000..45407b3 --- /dev/null +++ b/test/fork/deployment/PostDeployment.t.sol @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {Test} from "forge-std/Test.sol"; +import {Utilities} from "../../helpers/Utilities.sol"; +import {DeploymentFixtures} from "../../helpers/Fixtures.sol"; +import {DeployParams} from "../../../script/DeployBase.sol"; +import {OssifiableProxy} from "../../../src/lib/proxy/OssifiableProxy.sol"; +import {CCR} from "../../../src/CCR.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +contract CSModuleDeploymentTest is Test, Utilities, DeploymentFixtures { + DeployParams private deployParams; + + function setUp() public { + Env memory env = envVars(); + vm.createSelectFork(env.RPC_URL); + initializeFromDeployment(env.DEPLOY_CONFIG); + deployParams = parseDeployParams(env.DEPLOY_CONFIG); + } + + function test_constructor() public view { + assertEq(address(ccr.LIDO_LOCATOR()), deployParams.lidoLocatorAddress); + } + + function test_initializer() public view { + (uint64 optInDelayBlocks, uint64 optOutDelayBlocks, uint64 defaultOperatorMaxKeys, uint64 defaultBlockGasLimit) + = ccr.getConfig(); + + assertEq(optInDelayBlocks, deployParams.optInDelayBlocks); + assertEq(optOutDelayBlocks, deployParams.optOutDelayBlocks); + assertEq(defaultOperatorMaxKeys, deployParams.defaultOperatorMaxKeys); + assertEq(defaultBlockGasLimit, deployParams.defaultBlockGasLimit); + assertEq(ccr.getContractVersion(), 1); + assertFalse(ccr.paused()); + } + + function test_roles() public view { + assertTrue(ccr.hasRole(ccr.DEFAULT_ADMIN_ROLE(), deployParams.committeeAddress)); + assertTrue(ccr.getRoleMemberCount(ccr.DEFAULT_ADMIN_ROLE()) == 1); + assertTrue(ccr.hasRole(ccr.PAUSE_ROLE(), deployParams.committeeAddress)); + assertTrue(ccr.hasRole(ccr.RESUME_ROLE(), deployParams.committeeAddress)); + assertEq(ccr.getRoleMemberCount(ccr.PAUSE_ROLE()), 1); + assertEq(ccr.getRoleMemberCount(ccr.RESUME_ROLE()), 1); + } + + function test_proxy() public { + OssifiableProxy proxy = OssifiableProxy(payable(address(ccr))); + assertEq(proxy.proxy__getAdmin(), address(deployParams.proxyAdmin)); + assertFalse(proxy.proxy__getIsOssified()); + + CCR ccrImpl = CCR(proxy.proxy__getImplementation()); + assertEq(ccrImpl.getContractVersion(), type(uint64).max); + + vm.expectRevert(Initializable.InvalidInitialization.selector); + ccr.initialize({ + committeeAddress: deployParams.committeeAddress, + optInDelayBlocks: deployParams.optInDelayBlocks, + optOutDelayBlocks: deployParams.optOutDelayBlocks, + defaultOperatorMaxKeys: deployParams.defaultOperatorMaxKeys, + defaultBlockGasLimit: deployParams.defaultBlockGasLimit + }); + } +} diff --git a/test/fork/deployment/Upgradability.sol b/test/fork/deployment/Upgradability.sol new file mode 100644 index 0000000..2db6efa --- /dev/null +++ b/test/fork/deployment/Upgradability.sol @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {Test} from "forge-std/Test.sol"; +import {OssifiableProxy} from "../../../src/lib/proxy/OssifiableProxy.sol"; +import {CCRMock} from "../../helpers/mocks/CCRMock.sol"; +import {DeploymentFixtures} from "../../helpers/Fixtures.sol"; + +contract UpgradabilityTest is Test, DeploymentFixtures { + constructor() { + Env memory env = envVars(); + vm.createSelectFork(env.RPC_URL); + initializeFromDeployment(env.DEPLOY_CONFIG); + } + + function test_CCRUpgradeTo() public { + OssifiableProxy proxy = OssifiableProxy(payable(address(ccr))); + CCRMock newCcr = new CCRMock(address(ccr.LIDO_LOCATOR()), "csm-type-new"); + + vm.prank(proxy.proxy__getAdmin()); + proxy.proxy__upgradeTo(address(newCcr)); + assertEq(CCRMock(address(ccr)).__test__getCSModuleType(), "csm-type-new"); + } + + function test_CCRUpgradeToAndCall() public { + OssifiableProxy proxy = OssifiableProxy(payable(address(ccr))); + CCRMock newCcr = new CCRMock(address(ccr.LIDO_LOCATOR()), "csm-type-new"); + + address contractAdmin = ccr.getRoleMember(ccr.DEFAULT_ADMIN_ROLE(), 0); + vm.prank(contractAdmin); + ccr.pause(); + assertTrue(ccr.paused()); + + vm.prank(proxy.proxy__getAdmin()); + proxy.proxy__upgradeToAndCall(address(newCcr), abi.encodeCall(newCcr.initialize_v2, ())); + assertEq(CCRMock(address(ccr)).__test__getCSModuleType(), "csm-type-new"); + assertEq(ccr.getContractVersion(), 2); + assertFalse(ccr.paused()); + } +} diff --git a/test/helpers/CCRCommon.sol b/test/helpers/CCRCommon.sol new file mode 100644 index 0000000..04123d0 --- /dev/null +++ b/test/helpers/CCRCommon.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.28; + +import {Test} from "forge-std/Test.sol"; +import {Fixtures} from "./Fixtures.sol"; +import {Utilities} from "./Utilities.sol"; +import {LidoLocatorMock} from "test/helpers/mocks/LidoLocatorMock.sol"; +import {StakingModuleMock} from "test/helpers/mocks/StakingModuleMock.sol"; +import {StakingRouterMock} from "test/helpers/mocks/StakingRouterMock.sol"; +import {CuratedModuleMock} from "test/helpers/mocks/CuratedModuleMock.sol"; +import {CSModuleMock} from "test/helpers/mocks/CSModuleMock.sol"; + +abstract contract CCRFixtures is Test, Fixtures, Utilities { + LidoLocatorMock public locator; + StakingRouterMock public sr; + CuratedModuleMock public nor; + CSModuleMock public csm; + + // address internal admin; + address internal stranger1; + address internal stranger2; + address internal noCsm1; + address internal noCsm1Manager; + address internal noCurated1; + address internal noCurated1Manager; + address internal committee; + + // uint64 optInDelayBlocks = 100; + // uint64 optOutDelayBlocks = 200; + // uint64 defaultOperatorMaxKeys = 100; + // uint64 defaultBlockGasLimit = 1000000; + + // function createNo(uint256 modId, address rewAddr) internal returns (uint256) { + // StakingModuleMock m = StakingModuleMock(sr.getStakingModule(modId).stakingModuleAddress); + // return createNo(csm, rewAddr, 1); + // } + + function createNo(StakingModuleMock m, address rewAddr, uint32 keysCount) internal returns (uint64) { + m.addNo(true, rewAddr, keysCount); + return uint64(m.getNodeOperatorsCount() - 1); + } + + function updateNoKeys(StakingModuleMock m, uint256 noId, uint32 keysCount) internal { + m.updNoKeys(noId, keysCount); + } + + function updateNoActive(StakingModuleMock m, uint256 noId, bool active) internal { + m.updNoActive(noId, active); + } +} + +contract CCRCommon is CCRFixtures { + function setUp() public virtual { + committee = nextAddress("COMMITTEE"); + stranger1 = nextAddress("STRANGER1"); + stranger2 = nextAddress("STRANGER2"); + + (locator, sr, nor, csm) = initLidoMock(); + } +} diff --git a/test/helpers/Fixtures.sol b/test/helpers/Fixtures.sol index 7c39bf9..efe0ca9 100644 --- a/test/helpers/Fixtures.sol +++ b/test/helpers/Fixtures.sol @@ -5,8 +5,8 @@ pragma solidity 0.8.28; import {StdCheats} from "forge-std/StdCheats.sol"; import {Test} from "forge-std/Test.sol"; -import {DeployParams} from "../../script/DeployBase.s.sol"; -import {CredibleCommitmentCurationProvider} from "../../src/CredibleCommitmentCurationProvider.sol"; +import {DeployParams} from "../../script/DeployBase.sol"; +import {CCR} from "../../src/CCR.sol"; import {ILidoLocator} from "../../src/interfaces/ILidoLocator.sol"; import {IStakingRouter} from "../../src/interfaces/IStakingRouter.sol"; @@ -53,11 +53,11 @@ contract DeploymentFixtures is StdCheats, Test { struct DeploymentConfig { uint256 chainId; - address cccp; + address ccr; address lidoLocator; } - CredibleCommitmentCurationProvider public cccp; + CCR public ccr; ILidoLocator public locator; IStakingRouter public stakingRouter; @@ -73,7 +73,7 @@ contract DeploymentFixtures is StdCheats, Test { DeploymentConfig memory deploymentConfig = parseDeploymentConfig(config); assertEq(deploymentConfig.chainId, block.chainid, "ChainId mismatch"); - cccp = CredibleCommitmentCurationProvider(deploymentConfig.cccp); + ccr = CCR(deploymentConfig.ccr); locator = ILidoLocator(deploymentConfig.lidoLocator); stakingRouter = IStakingRouter(locator.stakingRouter()); } @@ -81,8 +81,8 @@ contract DeploymentFixtures is StdCheats, Test { function parseDeploymentConfig(string memory config) public returns (DeploymentConfig memory deploymentConfig) { deploymentConfig.chainId = vm.parseJsonUint(config, ".ChainId"); - deploymentConfig.cccp = vm.parseJsonAddress(config, ".CCCP"); - vm.label(deploymentConfig.cccp, "csm"); + deploymentConfig.ccr = vm.parseJsonAddress(config, ".CCR"); + vm.label(deploymentConfig.ccr, "csm"); deploymentConfig.lidoLocator = vm.parseJsonAddress(config, ".LidoLocator"); vm.label(deploymentConfig.lidoLocator, "LidoLocator"); diff --git a/test/helpers/Utilities.sol b/test/helpers/Utilities.sol index d305f01..76bf1a7 100644 --- a/test/helpers/Utilities.sol +++ b/test/helpers/Utilities.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.28; import {CommonBase, Vm} from "forge-std/Base.sol"; +import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; /// @author madlabman contract Utilities is CommonBase { @@ -20,4 +21,10 @@ contract Utilities is CommonBase { vm.label(a, label); return a; } + + function expectRoleRevert(address account, bytes32 neededRole) internal { + vm.expectRevert( + abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, account, neededRole) + ); + } } diff --git a/test/helpers/mocks/CCCPMock.sol b/test/helpers/mocks/CCCPMock.sol deleted file mode 100644 index 7d7d237..0000000 --- a/test/helpers/mocks/CCCPMock.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Lido -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.28; - -import {CredibleCommitmentCurationProvider} from "../../../src/CredibleCommitmentCurationProvider.sol"; - -contract CCCPMock is CredibleCommitmentCurationProvider { - constructor(address lidoLocator, bytes32 csModuleType) - CredibleCommitmentCurationProvider(lidoLocator, csModuleType) - {} - - function __test__getCSModuleType() external view returns (bytes32) { - return CS_MODULE_TYPE; - } -} diff --git a/test/helpers/mocks/CCRMock.sol b/test/helpers/mocks/CCRMock.sol new file mode 100644 index 0000000..b2df96b --- /dev/null +++ b/test/helpers/mocks/CCRMock.sol @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {CCR} from "../../../src/CCR.sol"; + +contract CCRMock is CCR { + constructor(address lidoLocator, bytes32 csModuleType) CCR(lidoLocator, csModuleType) {} + + function __test__getCSModuleType() external view returns (bytes32) { + return CS_MODULE_TYPE; + } + + function initialize_v2() external reinitializer(2) { + _unpause(); + } +} diff --git a/test/helpers/mocks/CSModuleMock.sol b/test/helpers/mocks/CSModuleMock.sol index 43c8ad3..d1068e6 100644 --- a/test/helpers/mocks/CSModuleMock.sol +++ b/test/helpers/mocks/CSModuleMock.sol @@ -8,7 +8,7 @@ import {StakingModuleMock} from "./StakingModuleMock.sol"; contract CSModuleMock is StakingModuleMock, ICSModule { function getNodeOperator(uint256 id) public view returns (CSMNodeOperator memory no) { - no.totalAddedKeys = ops[id].totalAddedValidators; + no.totalAddedKeys = ops[id].totalAddedKeys; no.rewardAddress = ops[id].rewardAddress; } diff --git a/test/helpers/mocks/CuratedModuleMock.sol b/test/helpers/mocks/CuratedModuleMock.sol index 1371e96..a399fd0 100644 --- a/test/helpers/mocks/CuratedModuleMock.sol +++ b/test/helpers/mocks/CuratedModuleMock.sol @@ -15,19 +15,19 @@ contract CuratedModuleMock is StakingModuleMock, ICuratedModule { bool active, string memory name, address rewardAddress, - uint64 totalVettedValidators, - uint64 totalExitedValidators, - uint64 totalAddedValidators + uint64 totalVettedKeys, + uint64 totalExitedKeys, + uint64 totalAddedKeys ) { active = ops[id].active; rewardAddress = ops[id].rewardAddress; - totalAddedValidators = ops[id].totalAddedValidators; + totalAddedKeys = ops[id].totalAddedKeys; // silence warnings name; - totalExitedValidators; - totalVettedValidators; + totalExitedKeys; + totalVettedKeys; } function getType() external pure returns (bytes32 moduleType) { diff --git a/test/helpers/mocks/StakingModuleMock.sol b/test/helpers/mocks/StakingModuleMock.sol index 5e182cb..d8d98de 100644 --- a/test/helpers/mocks/StakingModuleMock.sol +++ b/test/helpers/mocks/StakingModuleMock.sol @@ -9,7 +9,7 @@ abstract contract StakingModuleMock is IStakingModule { struct NO { bool active; address rewardAddress; - uint32 totalAddedValidators; + uint32 totalAddedKeys; } NO[] public ops; @@ -23,11 +23,11 @@ abstract contract StakingModuleMock is IStakingModule { function updNo(uint256 id, bool active, address rewAddr, uint32 keys) public { ops[id].active = active; ops[id].rewardAddress = rewAddr; - ops[id].totalAddedValidators = keys; + ops[id].totalAddedKeys = keys; } function updNoKeys(uint256 id, uint32 keys) public { - ops[id].totalAddedValidators = keys; + ops[id].totalAddedKeys = keys; } function updNoActive(uint256 id, bool active) public { diff --git a/yarn.lock b/yarn.lock index cd09b11..d59a7ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23,6 +23,27 @@ __metadata: languageName: node linkType: hard +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: "npm:^5.1.2" + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: "npm:^7.0.1" + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: "npm:^8.1.0" + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd + languageName: node + linkType: hard + "@pnpm/config.env-replace@npm:^1.1.0": version: 1.1.0 resolution: "@pnpm/config.env-replace@npm:1.1.0" @@ -57,6 +78,22 @@ __metadata: languageName: node linkType: hard +"@solidity-parser/parser@npm:^0.16.1": + version: 0.16.2 + resolution: "@solidity-parser/parser@npm:0.16.2" + dependencies: + antlr4ts: "npm:^0.5.0-alpha.4" + checksum: 10c0/f0612b36f9a25def75188b44ce06d7cb286b4f843c54b3f0e8836bdd48438663aafea7839890d54f9ccdbc6fa2c1e1247cae2ab734713463e21e4bd656e526a7 + languageName: node + linkType: hard + +"@solidity-parser/parser@npm:^0.18.0": + version: 0.18.0 + resolution: "@solidity-parser/parser@npm:0.18.0" + checksum: 10c0/c54b4c9ba10e1fd1cd45894040135a39b9bc527f0ac40bec732d8628b0c0c7cb7ec2b7e816b408d613ab1d71c04f9555111ccc83b6dbaed2e39ff4ef7d000e25 + languageName: node + linkType: hard + "@solidity-parser/parser@npm:^0.19.0": version: 0.19.0 resolution: "@solidity-parser/parser@npm:0.19.0" @@ -136,7 +173,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.2.1": +"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": version: 6.2.1 resolution: "ansi-styles@npm:6.2.1" checksum: 10c0/5d1ec38c123984bcedd996eac680d548f31828bd679a66db2bdf11844634dde55fec3efa9c6bb1d89056a5e79c1ac540c4c784d592ea1d25028a92227d2f2d5c @@ -150,6 +187,15 @@ __metadata: languageName: node linkType: hard +"antlr4ts@npm:^0.5.0-alpha.4": + version: 0.5.0-dev + resolution: "antlr4ts@npm:0.5.0-dev" + dependencies: + source-map-support: "npm:^0.5.16" + checksum: 10c0/948d95d02497a5751105cc61e9931d03a9bf0566b33a28ea8f2c72484a47ec4c5148670e1a525bfbc0069b1b86ab820417ec3fad120081211ff55f542fb4a835 + languageName: node + linkType: hard + "argparse@npm:^2.0.1": version: 2.0.1 resolution: "argparse@npm:2.0.1" @@ -171,6 +217,13 @@ __metadata: languageName: node linkType: hard +"async@npm:^3.2.4": + version: 3.2.6 + resolution: "async@npm:3.2.6" + checksum: 10c0/36484bb15ceddf07078688d95e27076379cc2f87b10c03b6dd8a83e89475a3c8df5848859dd06a4c95af1e4c16fc973de0171a77f18ea00be899aca2a4f85e70 + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -196,6 +249,20 @@ __metadata: languageName: node linkType: hard +"buffer-from@npm:^1.0.0": + version: 1.1.2 + resolution: "buffer-from@npm:1.1.2" + checksum: 10c0/124fff9d66d691a86d3b062eff4663fe437a9d9ee4b47b1b9e97f5a5d14f6d5399345db80f796827be7c95e70a8e765dd404b7c3ff3b3324f98e9b0c8826cc34 + languageName: node + linkType: hard + +"c3-linearization@npm:^0.3.0": + version: 0.3.0 + resolution: "c3-linearization@npm:0.3.0" + checksum: 10c0/7d07af10d6cc861e75f07fb55b7db3e97efc4b3e27921d047bb18a96dc18a147a78d6ab6e4195b6d25cab457d45158914942e7d5dc8d7cb150d13b16672adb03 + languageName: node + linkType: hard + "cacheable-lookup@npm:^7.0.0": version: 7.0.0 resolution: "cacheable-lookup@npm:7.0.0" @@ -235,7 +302,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:~5.4.1": +"chalk@npm:^5.4.1": version: 5.4.1 resolution: "chalk@npm:5.4.1" checksum: 10c0/b23e88132c702f4855ca6d25cb5538b1114343e41472d5263ee8a37cccfccd9c4216d111e1097c6a27830407a1dc81fecdf2a56f2c63033d4dbbd88c10b0dcef @@ -251,6 +318,15 @@ __metadata: languageName: node linkType: hard +"cli-table@npm:^0.3.11": + version: 0.3.11 + resolution: "cli-table@npm:0.3.11" + dependencies: + colors: "npm:1.0.3" + checksum: 10c0/6e31da4e19e942bf01749ff78d7988b01e0101955ce2b1e413eecdc115d4bb9271396464761491256a7d3feeedb5f37ae505f4314c4f8044b5d0f4b579c18f29 + languageName: node + linkType: hard + "cli-truncate@npm:^4.0.0": version: 4.0.0 resolution: "cli-truncate@npm:4.0.0" @@ -261,6 +337,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^8.0.1": + version: 8.0.1 + resolution: "cliui@npm:8.0.1" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5 + languageName: node + linkType: hard + "color-convert@npm:^2.0.1": version: 2.0.1 resolution: "color-convert@npm:2.0.1" @@ -284,6 +371,20 @@ __metadata: languageName: node linkType: hard +"colors@npm:1.0.3": + version: 1.0.3 + resolution: "colors@npm:1.0.3" + checksum: 10c0/f9e40dd8b3e1a65378a7ced3fced15ddfd60aaf38e99a7521a7fdb25056b15e092f651cd0f5aa1e9b04fa8ce3616d094e07fc6c2bb261e24098db1ddd3d09a1d + languageName: node + linkType: hard + +"colors@npm:^1.4.0": + version: 1.4.0 + resolution: "colors@npm:1.4.0" + checksum: 10c0/9af357c019da3c5a098a301cf64e3799d27549d8f185d86f79af23069e4f4303110d115da98483519331f6fb71c8568d5688fa1c6523600044fd4a54e97c4efb + languageName: node + linkType: hard + "commander@npm:^10.0.0": version: 10.0.1 resolution: "commander@npm:10.0.1" @@ -291,10 +392,17 @@ __metadata: languageName: node linkType: hard -"commander@npm:~12.1.0": - version: 12.1.0 - resolution: "commander@npm:12.1.0" - checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9 +"commander@npm:^11.0.0": + version: 11.1.0 + resolution: "commander@npm:11.1.0" + checksum: 10c0/13cc6ac875e48780250f723fb81c1c1178d35c5decb1abb1b628b3177af08a8554e76b2c0f29de72d69eef7c864d12613272a71fabef8047922bc622ab75a179 + languageName: node + linkType: hard + +"commander@npm:^13.1.0": + version: 13.1.0 + resolution: "commander@npm:13.1.0" + checksum: 10c0/7b8c5544bba704fbe84b7cab2e043df8586d5c114a4c5b607f83ae5060708940ed0b5bd5838cf8ce27539cde265c1cbd59ce3c8c6b017ed3eec8943e3a415164 languageName: node linkType: hard @@ -330,14 +438,16 @@ __metadata: resolution: "credible-commitment-curation-provider@workspace:." dependencies: husky: "npm:^9.1.7" - lint-staged: "npm:^15.3.0" + lint-staged: "npm:^15.4.2" prettier: "npm:^3.4.2" prettier-plugin-solidity: "npm:^1.4.2" - solhint: "npm:5.0.4" + solhint: "npm:5.0.5" + solhint-plugin-lido-csm: "https://github.com/lidofinance/solhint-plugin-lido-csm.git#0.3.3" + solidity-code-metrics: "npm:^0.0.28" languageName: unknown linkType: soft -"cross-spawn@npm:^7.0.3": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -348,7 +458,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:~4.4.0": +"debug@npm:^4.4.0": version: 4.4.0 resolution: "debug@npm:4.4.0" dependencies: @@ -383,6 +493,13 @@ __metadata: languageName: node linkType: hard +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 + languageName: node + linkType: hard + "emoji-regex@npm:^10.3.0": version: 10.4.0 resolution: "emoji-regex@npm:10.4.0" @@ -397,6 +514,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 + languageName: node + linkType: hard + "environment@npm:^1.0.0": version: 1.1.0 resolution: "environment@npm:1.1.0" @@ -413,6 +537,13 @@ __metadata: languageName: node linkType: hard +"escalade@npm:^3.1.1": + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 + languageName: node + linkType: hard + "eventemitter3@npm:^5.0.1": version: 5.0.1 resolution: "eventemitter3@npm:5.0.1" @@ -420,7 +551,7 @@ __metadata: languageName: node linkType: hard -"execa@npm:~8.0.1": +"execa@npm:^8.0.1": version: 8.0.1 resolution: "execa@npm:8.0.1" dependencies: @@ -474,6 +605,16 @@ __metadata: languageName: node linkType: hard +"foreground-child@npm:^3.1.0": + version: 3.3.0 + resolution: "foreground-child@npm:3.3.0" + dependencies: + cross-spawn: "npm:^7.0.0" + signal-exit: "npm:^4.0.1" + checksum: 10c0/028f1d41000553fcfa6c4bb5c372963bf3d9bf0b1f25a87d1a6253014343fb69dfb1b42d9625d7cf44c8ba429940f3d0ff718b62105d4d4a4f6ef8ca0a53faa2 + languageName: node + linkType: hard + "form-data-encoder@npm:^2.1.2": version: 2.1.4 resolution: "form-data-encoder@npm:2.1.4" @@ -488,6 +629,13 @@ __metadata: languageName: node linkType: hard +"get-caller-file@npm:^2.0.5": + version: 2.0.5 + resolution: "get-caller-file@npm:2.0.5" + checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde + languageName: node + linkType: hard + "get-east-asian-width@npm:^1.0.0": version: 1.3.0 resolution: "get-east-asian-width@npm:1.3.0" @@ -509,6 +657,22 @@ __metadata: languageName: node linkType: hard +"glob@npm:^10.3.15": + version: 10.4.5 + resolution: "glob@npm:10.4.5" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^3.1.2" + minimatch: "npm:^9.0.4" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^1.11.1" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e + languageName: node + linkType: hard + "glob@npm:^8.0.3": version: 8.1.0 resolution: "glob@npm:8.1.0" @@ -548,6 +712,15 @@ __metadata: languageName: node linkType: hard +"graphviz@npm:0.0.9": + version: 0.0.9 + resolution: "graphviz@npm:0.0.9" + dependencies: + temp: "npm:~0.4.0" + checksum: 10c0/d9e7ea3d74b00db43ae96fe465f988dccc2e62b48471bdfb134ea914f4ba552e5ec5817da350993e68b11452392d2fe9bfd79ad3d06ccaac6724301778c32870 + languageName: node + linkType: hard + "has-flag@npm:^4.0.0": version: 4.0.0 resolution: "has-flag@npm:4.0.0" @@ -555,6 +728,16 @@ __metadata: languageName: node linkType: hard +"hasha@npm:^5.2.0": + version: 5.2.2 + resolution: "hasha@npm:5.2.2" + dependencies: + is-stream: "npm:^2.0.0" + type-fest: "npm:^0.8.0" + checksum: 10c0/9d10d4e665a37beea6e18ba3a0c0399a05b26e505c5ff2fe9115b64fedb3ca95f68c89cf15b08ee4d09fd3064b5e1bfc8e8247353c7aa6b7388471d0f86dca74 + languageName: node + linkType: hard + "http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" @@ -666,6 +849,13 @@ __metadata: languageName: node linkType: hard +"is-stream@npm:^2.0.0": + version: 2.0.1 + resolution: "is-stream@npm:2.0.1" + checksum: 10c0/7c284241313fc6efc329b8d7f08e16c0efeb6baab1b4cd0ba579eb78e5af1aa5da11e68559896a2067cd6c526bd29241dda4eb1225e627d5aa1a89a76d4635a5 + languageName: node + linkType: hard + "is-stream@npm:^3.0.0": version: 3.0.0 resolution: "is-stream@npm:3.0.0" @@ -680,6 +870,19 @@ __metadata: languageName: node linkType: hard +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + "@pkgjs/parseargs": "npm:^0.11.0" + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9 + languageName: node + linkType: hard + "js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -744,7 +947,7 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:~3.1.3": +"lilconfig@npm:^3.1.3": version: 3.1.3 resolution: "lilconfig@npm:3.1.3" checksum: 10c0/f5604e7240c5c275743561442fbc5abf2a84ad94da0f5adc71d25e31fa8483048de3dcedcb7a44112a942fed305fd75841cdf6c9681c7f640c63f1049e9a5dcc @@ -758,27 +961,27 @@ __metadata: languageName: node linkType: hard -"lint-staged@npm:^15.3.0": - version: 15.3.0 - resolution: "lint-staged@npm:15.3.0" +"lint-staged@npm:^15.4.2": + version: 15.4.2 + resolution: "lint-staged@npm:15.4.2" dependencies: - chalk: "npm:~5.4.1" - commander: "npm:~12.1.0" - debug: "npm:~4.4.0" - execa: "npm:~8.0.1" - lilconfig: "npm:~3.1.3" - listr2: "npm:~8.2.5" - micromatch: "npm:~4.0.8" - pidtree: "npm:~0.6.0" - string-argv: "npm:~0.3.2" - yaml: "npm:~2.6.1" + chalk: "npm:^5.4.1" + commander: "npm:^13.1.0" + debug: "npm:^4.4.0" + execa: "npm:^8.0.1" + lilconfig: "npm:^3.1.3" + listr2: "npm:^8.2.5" + micromatch: "npm:^4.0.8" + pidtree: "npm:^0.6.0" + string-argv: "npm:^0.3.2" + yaml: "npm:^2.7.0" bin: lint-staged: bin/lint-staged.js - checksum: 10c0/1ddf9488c523c0b65c85b755428d4ad74fac3aa6ccb2e28e9bff5b8d86503158fe241d20d5433a11146872050b43580644901a5ef4c924b1ad7017c224a07339 + checksum: 10c0/08dd28149241788f7ca628a64c9c1817a9dfbe19517ba0317fdf96a1109f6d624948864edfeaf2936561bb49c65aeb32d5ddc75fb15afa2b6527024ef01a546b languageName: node linkType: hard -"listr2@npm:~8.2.5": +"listr2@npm:^8.2.5": version: 8.2.5 resolution: "listr2@npm:8.2.5" dependencies: @@ -826,6 +1029,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb + languageName: node + linkType: hard + "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -833,7 +1043,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:~4.0.8": +"micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -880,6 +1090,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.4": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed + languageName: node + linkType: hard + "minimist@npm:^1.2.0": version: 1.2.8 resolution: "minimist@npm:1.2.8" @@ -887,6 +1106,13 @@ __metadata: languageName: node linkType: hard +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557 + languageName: node + linkType: hard + "ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" @@ -944,6 +1170,13 @@ __metadata: languageName: node linkType: hard +"package-json-from-dist@npm:^1.0.0": + version: 1.0.1 + resolution: "package-json-from-dist@npm:1.0.1" + checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b + languageName: node + linkType: hard + "package-json@npm:^8.1.0": version: 8.1.1 resolution: "package-json@npm:8.1.1" @@ -991,6 +1224,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: "npm:^10.2.0" + minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" + checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d + languageName: node + linkType: hard + "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -1005,14 +1248,14 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^2.3.1": +"picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be languageName: node linkType: hard -"pidtree@npm:~0.6.0": +"pidtree@npm:^0.6.0": version: 0.6.0 resolution: "pidtree@npm:0.6.0" bin: @@ -1093,6 +1336,15 @@ __metadata: languageName: node linkType: hard +"readdirp@npm:^3.3.0": + version: 3.6.0 + resolution: "readdirp@npm:3.6.0" + dependencies: + picomatch: "npm:^2.2.1" + checksum: 10c0/6fa848cf63d1b82ab4e985f4cf72bd55b7dcfd8e0a376905804e48c3634b7e749170940ba77b32804d5fe93b3cc521aa95a8d7e7d725f830da6d93f3669ce66b + languageName: node + linkType: hard + "registry-auth-token@npm:^5.0.1": version: 5.0.3 resolution: "registry-auth-token@npm:5.0.3" @@ -1111,6 +1363,13 @@ __metadata: languageName: node linkType: hard +"require-directory@npm:^2.1.1": + version: 2.1.1 + resolution: "require-directory@npm:2.1.1" + checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99 + languageName: node + linkType: hard + "require-from-string@npm:^2.0.2": version: 2.0.2 resolution: "require-from-string@npm:2.0.2" @@ -1167,6 +1426,15 @@ __metadata: languageName: node linkType: hard +"sha1-file@npm:^2.0.0": + version: 2.0.1 + resolution: "sha1-file@npm:2.0.1" + dependencies: + hasha: "npm:^5.2.0" + checksum: 10c0/aa8a3ae2f64360163190adc75eebd2bf22a3ad8223069af3aaa88b6b1832fb71ce18df57cd2c67e1f7653fdc3011661d2ca90974b29e0d9d7b2f1844a14aba51 + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -1183,7 +1451,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^4.1.0": +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 @@ -1221,9 +1489,64 @@ __metadata: languageName: node linkType: hard -"solhint@npm:5.0.4": - version: 5.0.4 - resolution: "solhint@npm:5.0.4" +"sloc@npm:^0.3.2": + version: 0.3.2 + resolution: "sloc@npm:0.3.2" + dependencies: + async: "npm:^3.2.4" + cli-table: "npm:^0.3.11" + commander: "npm:^11.0.0" + readdirp: "npm:^3.3.0" + bin: + sloc: bin/sloc + checksum: 10c0/6287e561407c668f73139c2931db35c605aa471d5dade6dac81fb5d1659aed43f8464c629497556768df0fd4f692a5a8322283c9bb42b078433de6b021f8998d + languageName: node + linkType: hard + +"solhint-plugin-lido-csm@https://github.com/lidofinance/solhint-plugin-lido-csm.git#0.3.3": + version: 0.3.3 + resolution: "solhint-plugin-lido-csm@https://github.com/lidofinance/solhint-plugin-lido-csm.git#commit=db949adb893af939bfb1ca5ba191c26fd94aa69c" + dependencies: + solhint: "npm:4.5.2" + checksum: 10c0/3bbd7fcfd8adda38f8291586a0d29afcdd33bea20dc48b82cdd020696e6772f8c660bfa8468c8d9da4133b08d796bf08f7ef09f67b714c7a96a6193e7c3b6ce5 + languageName: node + linkType: hard + +"solhint@npm:4.5.2": + version: 4.5.2 + resolution: "solhint@npm:4.5.2" + dependencies: + "@solidity-parser/parser": "npm:^0.18.0" + ajv: "npm:^6.12.6" + antlr4: "npm:^4.13.1-patch-1" + ast-parents: "npm:^0.0.1" + chalk: "npm:^4.1.2" + commander: "npm:^10.0.0" + cosmiconfig: "npm:^8.0.0" + fast-diff: "npm:^1.2.0" + glob: "npm:^8.0.3" + ignore: "npm:^5.2.4" + js-yaml: "npm:^4.1.0" + latest-version: "npm:^7.0.0" + lodash: "npm:^4.17.21" + pluralize: "npm:^8.0.0" + prettier: "npm:^2.8.3" + semver: "npm:^7.5.2" + strip-ansi: "npm:^6.0.1" + table: "npm:^6.8.1" + text-table: "npm:^0.2.0" + dependenciesMeta: + prettier: + optional: true + bin: + solhint: solhint.js + checksum: 10c0/a54cc8df54ed711ca6be783b9291eb9e838f518f236d4df05c6ef412ead6a21f4a5bf3bace62ca7d2ed69f4566d5289db4c8c93e0c5e23ac91401904e1f1f312 + languageName: node + linkType: hard + +"solhint@npm:5.0.5": + version: 5.0.5 + resolution: "solhint@npm:5.0.5" dependencies: "@solidity-parser/parser": "npm:^0.19.0" ajv: "npm:^6.12.6" @@ -1249,18 +1572,61 @@ __metadata: optional: true bin: solhint: solhint.js - checksum: 10c0/70058b23c8746762fc88d48b571c4571719913ca7f3c582a55c123ad9ba38976a2338782025fbb9643bb75bfad18bf3dce1b71e500df6d99589e9814fbcce1d7 + checksum: 10c0/becf018ff57f6b3579a7001179dcf941814bbdbc9fed8e4bb6502d35a8b5adc4fc42d0fa7f800e3003471768f9e17d2c458fb9f21c65c067160573f16ff12769 + languageName: node + linkType: hard + +"solidity-code-metrics@npm:^0.0.28": + version: 0.0.28 + resolution: "solidity-code-metrics@npm:0.0.28" + dependencies: + "@solidity-parser/parser": "npm:^0.18.0" + glob: "npm:^10.3.15" + sloc: "npm:^0.3.2" + solidity-doppelganger: "npm:^0.0.11" + surya: "npm:^0.4.12" + bin: + solidity-code-metrics: src/cli.js + checksum: 10c0/cbd0de8b37ee342cf943975ab8963859f6b13e0f1a1df8635e37882bc0f085d506cc35e20413684ce36acfbbb116b35be99afe7bb42e39391f880e885cf09f9d languageName: node linkType: hard -"string-argv@npm:~0.3.2": +"solidity-doppelganger@npm:^0.0.11": + version: 0.0.11 + resolution: "solidity-doppelganger@npm:0.0.11" + dependencies: + "@solidity-parser/parser": "npm:^0.16.1" + glob: "npm:^8.0.3" + yargs: "npm:^17.0.1" + checksum: 10c0/43bc2ce9a2c5ab39d7a09ddd72f8939fe1f24cd353833ebc2fdba77bcb2d374c7fffee1d8cff74a9665cbdfc331feadab9e518722913772c2163c1b37163c21a + languageName: node + linkType: hard + +"source-map-support@npm:^0.5.16": + version: 0.5.21 + resolution: "source-map-support@npm:0.5.21" + dependencies: + buffer-from: "npm:^1.0.0" + source-map: "npm:^0.6.0" + checksum: 10c0/9ee09942f415e0f721d6daad3917ec1516af746a8120bba7bb56278707a37f1eb8642bde456e98454b8a885023af81a16e646869975f06afc1a711fb90484e7d + languageName: node + linkType: hard + +"source-map@npm:^0.6.0": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 + languageName: node + linkType: hard + +"string-argv@npm:^0.3.2": version: 0.3.2 resolution: "string-argv@npm:0.3.2" checksum: 10c0/75c02a83759ad1722e040b86823909d9a2fc75d15dd71ec4b537c3560746e33b5f5a07f7332d1e3f88319909f82190843aa2f0a0d8c8d591ec08e93d5b8dec82 languageName: node linkType: hard -"string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -1271,6 +1637,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: "npm:^0.2.0" + emoji-regex: "npm:^9.2.2" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca + languageName: node + linkType: hard + "string-width@npm:^7.0.0": version: 7.2.0 resolution: "string-width@npm:7.2.0" @@ -1282,7 +1659,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" dependencies: @@ -1291,7 +1668,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^7.1.0": +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": version: 7.1.0 resolution: "strip-ansi@npm:7.1.0" dependencies: @@ -1323,6 +1700,23 @@ __metadata: languageName: node linkType: hard +"surya@npm:^0.4.12": + version: 0.4.12 + resolution: "surya@npm:0.4.12" + dependencies: + "@solidity-parser/parser": "npm:^0.16.1" + c3-linearization: "npm:^0.3.0" + colors: "npm:^1.4.0" + graphviz: "npm:0.0.9" + sha1-file: "npm:^2.0.0" + treeify: "npm:^1.1.0" + yargs: "npm:^17.0.0" + bin: + surya: bin/surya + checksum: 10c0/ed90d4ba50390e33df65590c5e8f2bfcc444822202431baabe39abbac6486946770007d0aa4b2441e906ac1aa6fda26ac8efbab07bf4b83b1410587625641490 + languageName: node + linkType: hard + "table@npm:^6.8.1": version: 6.9.0 resolution: "table@npm:6.9.0" @@ -1336,6 +1730,13 @@ __metadata: languageName: node linkType: hard +"temp@npm:~0.4.0": + version: 0.4.0 + resolution: "temp@npm:0.4.0" + checksum: 10c0/cf25c604d7509c3a41d2893c9779d4130a127ac2a9bea23ec1a5ff7da0f82e0014b5dcd77a4ce7227e63bc667ad4bd5ec312441e01e39ca5dcee6e146fbdaefb + languageName: node + linkType: hard + "text-table@npm:^0.2.0": version: 0.2.0 resolution: "text-table@npm:0.2.0" @@ -1352,6 +1753,20 @@ __metadata: languageName: node linkType: hard +"treeify@npm:^1.1.0": + version: 1.1.0 + resolution: "treeify@npm:1.1.0" + checksum: 10c0/2f0dea9e89328b8a42296a3963d341ab19897a05b723d6b0bced6b28701a340d2a7b03241aef807844198e46009aaf3755139274eb082cfce6fdc1935cbd69dd + languageName: node + linkType: hard + +"type-fest@npm:^0.8.0": + version: 0.8.1 + resolution: "type-fest@npm:0.8.1" + checksum: 10c0/dffbb99329da2aa840f506d376c863bd55f5636f4741ad6e65e82f5ce47e6914108f44f340a0b74009b0cb5d09d6752ae83203e53e98b1192cf80ecee5651636 + languageName: node + linkType: hard + "uri-js@npm:^4.2.2": version: 4.4.1 resolution: "uri-js@npm:4.4.1" @@ -1372,6 +1787,28 @@ __metadata: languageName: node linkType: hard +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da + languageName: node + linkType: hard + +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: "npm:^6.1.0" + string-width: "npm:^5.0.1" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 + languageName: node + linkType: hard + "wrap-ansi@npm:^9.0.0": version: 9.0.0 resolution: "wrap-ansi@npm:9.0.0" @@ -1390,11 +1827,40 @@ __metadata: languageName: node linkType: hard -"yaml@npm:~2.6.1": - version: 2.6.1 - resolution: "yaml@npm:2.6.1" +"y18n@npm:^5.0.5": + version: 5.0.8 + resolution: "y18n@npm:5.0.8" + checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249 + languageName: node + linkType: hard + +"yaml@npm:^2.7.0": + version: 2.7.0 + resolution: "yaml@npm:2.7.0" bin: yaml: bin.mjs - checksum: 10c0/aebf07f61c72b38c74d2b60c3a3ccf89ee4da45bcd94b2bfb7899ba07a5257625a7c9f717c65a6fc511563d48001e01deb1d9e55f0133f3e2edf86039c8c1be7 + checksum: 10c0/886a7d2abbd70704b79f1d2d05fe9fb0aa63aefb86e1cb9991837dced65193d300f5554747a872b4b10ae9a12bc5d5327e4d04205f70336e863e35e89d8f4ea9 + languageName: node + linkType: hard + +"yargs-parser@npm:^21.1.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2 + languageName: node + linkType: hard + +"yargs@npm:^17.0.0, yargs@npm:^17.0.1": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" + dependencies: + cliui: "npm:^8.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.3" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^21.1.1" + checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 languageName: node linkType: hard