diff --git a/addresses/Addresses.sol b/addresses/Addresses.sol index d52654e2..5a64f1c2 100644 --- a/addresses/Addresses.sol +++ b/addresses/Addresses.sol @@ -14,9 +14,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND pragma solidity ^0.8.0; -import {console} from "@forge-std/console.sol"; -import {Test} from "@forge-std/Test.sol"; import {IAddresses} from "@addresses/IAddresses.sol"; +import {Test} from "@forge-std/Test.sol"; +import {console} from "@forge-std/console.sol"; /// @notice This is a contract that stores addresses for different networks. /// It allows a project to have a single source of truth to get all the addresses @@ -28,13 +28,11 @@ contract Addresses is IAddresses, Test { } /// @notice mapping from contract name to network chain id to address - mapping(string name => mapping(uint256 chainId => Address)) public - _addresses; + mapping(string name => mapping(uint256 chainId => Address)) public _addresses; /// each address on each network should only have 1 name /// @notice mapping from address to chain id to whether it exists - mapping(address addr => mapping(uint256 chainId => bool exist)) public - addressToChainId; + mapping(address addr => mapping(uint256 chainId => bool exist)) public addressToChainId; /// @notice json structure to store address details struct SavedAddresses { @@ -86,38 +84,22 @@ contract Addresses is IAddresses, Test { /// @notice addresses chain ids uint256[] private chainIds; - constructor( - string memory _addressesFolderPath, - uint256[] memory _chainIds - ) { + constructor(string memory _addressesFolderPath, uint256[] memory _chainIds) { addressesFolderPath = _addressesFolderPath; for (uint256 i; i < _chainIds.length; ++i) { chainIds.push(_chainIds[i]); - string memory addressesPath = string( - abi.encodePacked( - _addressesFolderPath, - "/", - vm.toString(_chainIds[i]), - ".json" - ) - ); + string memory addressesPath = + string(abi.encodePacked(_addressesFolderPath, "/", vm.toString(_chainIds[i]), ".json")); - string memory addressesData = - string(abi.encodePacked(vm.readFile(addressesPath))); + string memory addressesData = string(abi.encodePacked(vm.readFile(addressesPath))); bytes memory parsedJson = vm.parseJson(addressesData); - FileAddresses[] memory fileAddresses = - abi.decode(parsedJson, (FileAddresses[])); + FileAddresses[] memory fileAddresses = abi.decode(parsedJson, (FileAddresses[])); for (uint256 j = 0; j < fileAddresses.length; j++) { - _addAddress( - fileAddresses[j].name, - fileAddresses[j].addr, - _chainIds[i], - fileAddresses[j].isContract - ); + _addAddress(fileAddresses[j].name, fileAddresses[j].addr, _chainIds[i], fileAddresses[j].isContract); } } } @@ -131,11 +113,7 @@ contract Addresses is IAddresses, Test { /// @notice get an address for a specific chainId /// @param name the name of the address /// @param _chainId the chain id - function getAddress(string memory name, uint256 _chainId) - public - view - returns (address) - { + function getAddress(string memory name, uint256 _chainId) public view returns (address) { return _getAddress(name, _chainId); } @@ -143,14 +121,10 @@ contract Addresses is IAddresses, Test { /// @param name the name of the address /// @param addr the address to add /// @param isContract whether the address is a contract - function addAddress(string memory name, address addr, bool isContract) - public - { + function addAddress(string memory name, address addr, bool isContract) public { _addAddress(name, addr, block.chainid, isContract); - recordedAddresses.push( - RecordedAddress({name: name, chainId: block.chainid}) - ); + recordedAddresses.push(RecordedAddress({name: name, chainId: block.chainid})); } /// @notice add an address for a specific chainId @@ -158,12 +132,7 @@ contract Addresses is IAddresses, Test { /// @param addr the address to add /// @param _chainId the chain id /// @param isContract whether the address is a contract - function addAddress( - string memory name, - address addr, - uint256 _chainId, - bool isContract - ) public { + function addAddress(string memory name, address addr, uint256 _chainId, bool isContract) public { _addAddress(name, addr, _chainId, isContract); recordedAddresses.push(RecordedAddress({name: name, chainId: _chainId})); @@ -174,12 +143,7 @@ contract Addresses is IAddresses, Test { /// @param _addr the address to change to /// @param chainId the chain id /// @param isContract whether the address is a contract - function changeAddress( - string memory name, - address _addr, - uint256 chainId, - bool isContract - ) public { + function changeAddress(string memory name, address _addr, uint256 chainId, bool isContract) public { Address storage data = _addresses[name][chainId]; require(_addr != address(0), "Address cannot be 0"); @@ -190,11 +154,7 @@ contract Addresses is IAddresses, Test { data.addr != address(0), string( abi.encodePacked( - "Address: ", - name, - " doesn't exist on chain: ", - vm.toString(chainId), - ". Use addAddress instead" + "Address: ", name, " doesn't exist on chain: ", vm.toString(chainId), ". Use addAddress instead" ) ) ); @@ -202,31 +162,23 @@ contract Addresses is IAddresses, Test { require( data.addr != _addr, string( - abi.encodePacked( - "Address: ", - name, - " already set to the same value on chain: ", - vm.toString(chainId) - ) + abi.encodePacked("Address: ", name, " already set to the same value on chain: ", vm.toString(chainId)) ) ); + addressToChainId[data.addr][chainId] = false; _checkAddress(_addr, isContract, name, chainId); - changedAddresses.push( - ChangedAddress({name: name, chainId: chainId, oldAddress: data.addr}) - ); + changedAddresses.push(ChangedAddress({name: name, chainId: chainId, oldAddress: data.addr})); for (uint256 i; i < savedAddresses.length; i++) { if ( - keccak256(abi.encode(savedAddresses[i].name)) - == keccak256(abi.encode(name)) + keccak256(abi.encode(savedAddresses[i].name)) == keccak256(abi.encode(name)) && savedAddresses[i].chainId == chainId ) { savedAddresses[i].addr = _addr; } } - data.addr = _addr; data.isContract = isContract; vm.label(_addr, name); @@ -236,9 +188,7 @@ contract Addresses is IAddresses, Test { /// @param name the name of the address /// @param addr the address to change to /// @param isContract whether the address is a contract - function changeAddress(string memory name, address addr, bool isContract) - public - { + function changeAddress(string memory name, address addr, bool isContract) public { changeAddress(name, addr, block.chainid, isContract); } @@ -251,11 +201,7 @@ contract Addresses is IAddresses, Test { function getRecordedAddresses() public view - returns ( - string[] memory names, - uint256[] memory chainIdsList, - address[] memory addresses - ) + returns (string[] memory names, uint256[] memory chainIdsList, address[] memory addresses) { uint256 length = recordedAddresses.length; names = new string[](length); @@ -265,8 +211,7 @@ contract Addresses is IAddresses, Test { for (uint256 i = 0; i < length; i++) { names[i] = recordedAddresses[i].name; chainIdsList[i] = recordedAddresses[i].chainId; - addresses[i] = _addresses[recordedAddresses[i].name][recordedAddresses[i] - .chainId].addr; + addresses[i] = _addresses[recordedAddresses[i].name][recordedAddresses[i].chainId].addr; } } @@ -296,8 +241,7 @@ contract Addresses is IAddresses, Test { names[i] = changedAddresses[i].name; chainIdsList[i] = changedAddresses[i].chainId; oldAddresses[i] = changedAddresses[i].oldAddress; - newAddresses[i] = _addresses[changedAddresses[i].name][changedAddresses[i] - .chainId].addr; + newAddresses[i] = _addresses[changedAddresses[i].name][changedAddresses[i].chainId].addr; } } @@ -316,37 +260,27 @@ contract Addresses is IAddresses, Test { /// @notice check if an address is set for a specific chain id /// @param name the name of the address /// @param chainId the chain id - function isAddressSet(string memory name, uint256 chainId) - public - view - returns (bool) - { + function isAddressSet(string memory name, uint256 chainId) public view returns (bool) { return _addresses[name][chainId].addr != address(0); } /// @dev Print new recorded and changed addresses function printJSONChanges() external view { { - (string[] memory names,, address[] memory addresses) = - getRecordedAddresses(); + (string[] memory names,, address[] memory addresses) = getRecordedAddresses(); if (names.length > 0) { console.log("\n\n------------------ Addresses Added ------------------"); for (uint256 j = 0; j < names.length; j++) { console.log("{\n \"addr\": \"%s\", ", addresses[j]); console.log(" \"isContract\": %s,", true); - console.log( - " \"name\": \"%s\"\n}%s", - names[j], - j < names.length - 1 ? "," : "" - ); + console.log(" \"name\": \"%s\"\n}%s", names[j], j < names.length - 1 ? "," : ""); } } } { - (string[] memory names,,, address[] memory addresses) = - getChangedAddresses(); + (string[] memory names,,, address[] memory addresses) = getChangedAddresses(); if (names.length > 0) { console.log("\n\n----------------- Addresses changed -----------------"); @@ -355,11 +289,7 @@ contract Addresses is IAddresses, Test { console.log("{\n 'addr': '%s', ", addresses[j]); console.log(" 'chainId': %d,", block.chainid); console.log(" 'isContract': %s", true, ","); - console.log( - " 'name': '%s'\n}%s", - names[j], - j < names.length - 1 ? "," : "" - ); + console.log(" 'name': '%s'\n}%s", names[j], j < names.length - 1 ? "," : ""); } } } @@ -369,11 +299,8 @@ contract Addresses is IAddresses, Test { function updateJson() external { for (uint256 i; i < chainIds.length; ++i) { string memory json = _constructJson(chainIds[i]); - string memory addressesPath = string( - abi.encodePacked( - addressesFolderPath, "/", vm.toString(chainIds[i]), ".json" - ) - ); + string memory addressesPath = + string(abi.encodePacked(addressesFolderPath, "/", vm.toString(chainIds[i]), ".json")); vm.writeJson(json, addressesPath); } } @@ -383,12 +310,7 @@ contract Addresses is IAddresses, Test { /// @param addr the address to add /// @param chainId the chain id /// @param isContract whether the address is a contract - function _addAddress( - string memory name, - address addr, - uint256 chainId, - bool isContract - ) private { + function _addAddress(string memory name, address addr, uint256 chainId, bool isContract) private { Address storage currentAddress = _addresses[name][chainId]; require(addr != address(0), "Address cannot be 0"); @@ -397,28 +319,14 @@ contract Addresses is IAddresses, Test { require( currentAddress.addr == address(0), - string( - abi.encodePacked( - "Address with name: ", - name, - " already set on chain: ", - vm.toString(chainId) - ) - ) + string(abi.encodePacked("Address with name: ", name, " already set on chain: ", vm.toString(chainId))) ); bool exist = addressToChainId[addr][chainId]; require( !exist, - string( - abi.encodePacked( - "Address: ", - vm.toString(addr), - " already set on chain: ", - vm.toString(chainId) - ) - ) + string(abi.encodePacked("Address: ", vm.toString(addr), " already set on chain: ", vm.toString(chainId))) ); addressToChainId[addr][chainId] = true; @@ -428,14 +336,7 @@ contract Addresses is IAddresses, Test { currentAddress.addr = addr; currentAddress.isContract = isContract; - savedAddresses.push( - SavedAddresses({ - name: name, - addr: addr, - chainId: chainId, - isContract: isContract - }) - ); + savedAddresses.push(SavedAddresses({name: name, addr: addr, chainId: chainId, isContract: isContract})); vm.label(addr, name); } @@ -443,26 +344,14 @@ contract Addresses is IAddresses, Test { /// @notice get an address for a specific chainId /// @param name the name of the address /// @param chainId the chain id - function _getAddress(string memory name, uint256 chainId) - private - view - returns (address addr) - { + function _getAddress(string memory name, uint256 chainId) private view returns (address addr) { require(chainId != 0, "ChainId cannot be 0"); Address memory data = _addresses[name][chainId]; addr = data.addr; require( - addr != address(0), - string( - abi.encodePacked( - "Address: ", - name, - " not set on chain: ", - vm.toString(chainId) - ) - ) + addr != address(0), string(abi.encodePacked("Address: ", name, " not set on chain: ", vm.toString(chainId))) ); } @@ -471,36 +360,17 @@ contract Addresses is IAddresses, Test { /// @param isContract whether the address is a contract /// @param name the name of the address /// @param chainId the chain id - function _checkAddress( - address _addr, - bool isContract, - string memory name, - uint256 chainId - ) private view { + function _checkAddress(address _addr, bool isContract, string memory name, uint256 chainId) private view { if (chainId == block.chainid) { if (isContract) { require( _addr.code.length > 0, - string( - abi.encodePacked( - "Address: ", - name, - " is not a contract on chain: ", - vm.toString(chainId) - ) - ) + string(abi.encodePacked("Address: ", name, " is not a contract on chain: ", vm.toString(chainId))) ); } else { require( _addr.code.length == 0, - string( - abi.encodePacked( - "Address: ", - name, - " is a contract on chain: ", - vm.toString(chainId) - ) - ) + string(abi.encodePacked("Address: ", name, " is a contract on chain: ", vm.toString(chainId))) ); } } @@ -508,11 +378,7 @@ contract Addresses is IAddresses, Test { /// @notice constructs json string data for address json from saved addresses array /// @param chainId chain id of addresses - function _constructJson(uint256 chainId) - private - view - returns (string memory) - { + function _constructJson(uint256 chainId) private view returns (string memory) { string memory json = "["; for (uint256 i = 0; i < savedAddresses.length; ++i) { @@ -544,11 +410,7 @@ contract Addresses is IAddresses, Test { return json; } - function _removeLastCharacter(string memory str) - public - pure - returns (string memory) - { + function _removeLastCharacter(string memory str) public pure returns (string memory) { bytes memory strBytes = bytes(str); // Create a new bytes array with one less byte diff --git a/src/proposals/MultisigProposal.sol b/src/proposals/MultisigProposal.sol index e508bba5..44d3b4fb 100644 --- a/src/proposals/MultisigProposal.sol +++ b/src/proposals/MultisigProposal.sol @@ -9,9 +9,6 @@ import {Constants} from "@utils/Constants.sol"; abstract contract MultisigProposal is Proposal { using Address for address; - bytes32 public constant MULTISIG_BYTECODE_HASH = - bytes32(0xb89c1b3bdf2cf8827818646bce9a8f6e372885f8c55e5c07acbd307cb133b000); - struct Call3Value { address target; bool allowFailure; @@ -19,21 +16,52 @@ abstract contract MultisigProposal is Proposal { bytes callData; } + /// @notice get operations for each action, override this to provide custom operations + function getOperations() + public + view + virtual + returns (uint8[] memory operations) + { + uint256 actionsLength = actions.length; + operations = new uint8[](actionsLength); + + // Default all operations to 0 (Call operation) + for (uint256 i = 0; i < actionsLength; i++) { + operations[i] = 0; + } + } + /// @notice return calldata, log if debug is set to true function getCalldata() public view override returns (bytes memory) { - (address[] memory targets, uint256[] memory values, bytes[] memory arguments) = getProposalActions(); - - require(targets.length == values.length && values.length == arguments.length, "Array lengths mismatch"); + ( + address[] memory targets, + uint256[] memory values, + bytes[] memory arguments + ) = getProposalActions(); + uint8[] memory operations = getOperations(); + + require( + targets.length == values.length && values.length == arguments.length + && arguments.length == operations.length + && operations.length == actions.length, + "Array lengths mismatch" + ); bytes memory encodedTxs; for (uint256 i = 0; i < targets.length; i++) { - uint8 operation = 0; + uint8 operation = operations[i]; address to = targets[i]; uint256 value = values[i]; bytes memory data = arguments[i]; - encodedTxs = bytes.concat(encodedTxs, abi.encodePacked(operation, to, value, uint256(data.length), data)); + encodedTxs = bytes.concat( + encodedTxs, + abi.encodePacked( + operation, to, value, uint256(data.length), data + ) + ); } // The final calldata to send to the MultiSend contract @@ -49,16 +77,34 @@ abstract contract MultisigProposal is Proposal { function _simulateActions(address multisig) internal { vm.startPrank(multisig); - /// this is a hack because multisig execTransaction requires owners signatures - /// so we cannot simulate it exactly as it will be executed on mainnet - vm.etch(multisig, Constants.MULTISEND_BYTECODE); - bytes memory data = getCalldata(); - multisig.functionCall(data); + // this is a hack because multisig execTransaction requires owners signatures + // so the SAFE_CREATION_BYTECODE override the checkNSignatures function + bytes memory bytecode = Constants.SAFE_CREATION_BYTECODE; + address addr; + /// @solidity memory-safe-assembly + assembly { + addr := create(0, add(bytecode, 0x20), mload(bytecode)) + } - /// revert contract code to original safe bytecode - vm.etch(multisig, Constants.SAFE_BYTECODE); + vm.etch(multisig, address(addr).code); + + bytes memory safeCalldata = abi.encodeWithSignature( + "execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes)", + Constants.SAFE_MULTISEND_COTNRACT, + 0, + data, + 1, + 0, + 0, + 0, + address(0), + address(0), + "" + ); + + multisig.functionCall(safeCalldata); vm.stopPrank(); } diff --git a/src/proposals/Proposal.sol b/src/proposals/Proposal.sol index 6ec2fa1c..fe476d95 100644 --- a/src/proposals/Proposal.sol +++ b/src/proposals/Proposal.sol @@ -4,9 +4,9 @@ import {Test} from "@forge-std/Test.sol"; import {VmSafe} from "@forge-std/Vm.sol"; import {console} from "@forge-std/console.sol"; +import {Addresses} from "@addresses/Addresses.sol"; import {Script} from "@forge-std/Script.sol"; import {IProposal} from "@proposals/IProposal.sol"; -import {Addresses} from "@addresses/Addresses.sol"; abstract contract Proposal is Test, Script, IProposal { struct Action { @@ -363,10 +363,10 @@ abstract contract Proposal is Test, Script, IProposal { /// static calls are ignored, /// calls to and from Addresses and the vm contract are ignored if ( + /// ignore calls to vm in the build function accountAccesses[i].account != address(addresses) && accountAccesses[i].account != address(vm) - /// ignore calls to vm in the build function - && accountAccesses[i].accessor != address(addresses) + && accountAccesses[i].accessor != address(addresses) && accountAccesses[i].kind == VmSafe.AccountAccessKind.Call && accountAccesses[i].accessor == caller ) { diff --git a/utils/Constants.sol b/utils/Constants.sol index 1c7025fd..acc5b5fc 100644 --- a/utils/Constants.sol +++ b/utils/Constants.sol @@ -4,6 +4,11 @@ library Constants { bytes public constant MULTISEND_BYTECODE = hex"60806040526004361061001e5760003560e01c80638d80ff0a14610023575b600080fd5b6100dc6004803603602081101561003957600080fd5b810190808035906020019064010000000081111561005657600080fd5b82018360208201111561006857600080fd5b8035906020019184600183028401116401000000008311171561008a57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506100de565b005b805160205b8181101561015f578083015160f81c6001820184015160601c60158301850151603584018601516055850187016000856000811461012857600181146101385761013d565b6000808585888a5af1915061013d565b600080fd5b50600081141561014c57600080fd5b82605501870196505050505050506100e3565b50505056fea264697066735822122035246402746c96964495cae5b36461fd44dfb89f8e6cf6f6b8d60c0aa89f414864736f6c63430007060033"; - bytes public constant SAFE_BYTECODE = - hex"608060405273ffffffffffffffffffffffffffffffffffffffff600054167fa619486e0000000000000000000000000000000000000000000000000000000060003514156050578060005260206000f35b3660008037600080366000845af43d6000803e60008114156070573d6000fd5b3d6000f3fea2646970667358221220d1429297349653a4918076d650332de1a1068c5f3e07c5c82360c277770b955264736f6c63430007060033"; + address constant SAFE_MULTISEND_COTNRACT = + // 0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761; // Multisend + 0x40A2aCCbd92BCA938b02010E17A5b8929b49130D; // MultisendCallOnly + + // This is a modified Safe that overrides the checkNSignatures function + bytes public constant SAFE_CREATION_BYTECODE = + hex"608060405234801561001057600080fd5b506001600455613250806100256000396000f3fe6080604052600436106101d15760003560e01c8063affed0e0116100f7578063e19a9dd911610095578063f08a032311610064578063f08a032314610fda578063f698da251461100d578063f8dc5dd914611022578063ffa1ad74146110655761020e565b8063e19a9dd914610e88578063e318b52b14610ebb578063e75235b814610f00578063e86637db14610f155761020e565b8063cc2f8452116100d1578063cc2f845214610cc1578063d4d9bdcd14610d5e578063d8d11f7814610d88578063e009cfde14610e4d5761020e565b8063affed0e014610aeb578063b4faba0914610b00578063b63e800d14610bc15761020e565b80635624b25b1161016f5780636a7612021161013e5780636a761202146107d25780637d83297414610910578063934f3a1114610949578063a0e67e2b14610a865761020e565b80635624b25b146106a65780635ae6bd371461074b578063610b592514610775578063694e80c3146107a85761020e565b80632f54bf6e116101ab5780632f54bf6e146104355780633408e47014610468578063468721a71461048f5780635229073f1461055a5761020e565b80630d582f131461027457806312fb68e0146102af5780632d9ad53d146103ee5761020e565b3661020e5760408051348152905133917f3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d919081900360200190a2005b34801561021a57600080fd5b507f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d580548061024557005b36600080373360601b365260008060143601600080855af190503d6000803e8061026e573d6000fd5b503d6000f35b34801561028057600080fd5b506102ad6004803603604081101561029757600080fd5b506001600160a01b03813516906020013561107a565b005b3480156102bb57600080fd5b506102ad600480360360808110156102d257600080fd5b81359190810190604081016020820135600160201b8111156102f357600080fd5b82018360208201111561030557600080fd5b803590602001918460018302840111600160201b8311171561032657600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b81111561037857600080fd5b82018360208201111561038a57600080fd5b803590602001918460018302840111600160201b831117156103ab57600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955050913592506111f0915050565b3480156103fa57600080fd5b506104216004803603602081101561041157600080fd5b50356001600160a01b03166111f6565b604080519115158252519081900360200190f35b34801561044157600080fd5b506104216004803603602081101561045857600080fd5b50356001600160a01b0316611231565b34801561047457600080fd5b5061047d611269565b60408051918252519081900360200190f35b34801561049b57600080fd5b50610421600480360360808110156104b257600080fd5b6001600160a01b0382351691602081013591810190606081016040820135600160201b8111156104e157600080fd5b8201836020820111156104f357600080fd5b803590602001918460018302840111600160201b8311171561051457600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295505050903560ff16915061126d9050565b34801561056657600080fd5b506106256004803603608081101561057d57600080fd5b6001600160a01b0382351691602081013591810190606081016040820135600160201b8111156105ac57600080fd5b8201836020820111156105be57600080fd5b803590602001918460018302840111600160201b831117156105df57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295505050903560ff16915061135a9050565b60405180831515815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561066a578181015183820152602001610652565b50505050905090810190601f1680156106975780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b3480156106b257600080fd5b506106d6600480360360408110156106c957600080fd5b5080359060200135611390565b6040805160208082528351818301528351919283929083019185019080838360005b838110156107105781810151838201526020016106f8565b50505050905090810190601f16801561073d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561075757600080fd5b5061047d6004803603602081101561076e57600080fd5b5035611404565b34801561078157600080fd5b506102ad6004803603602081101561079857600080fd5b50356001600160a01b0316611416565b3480156107b457600080fd5b506102ad600480360360208110156107cb57600080fd5b5035611692565b61042160048036036101408110156107e957600080fd5b6001600160a01b0382351691602081013591810190606081016040820135600160201b81111561081857600080fd5b82018360208201111561082a57600080fd5b803590602001918460018302840111600160201b8311171561084b57600080fd5b9193909260ff833516926020810135926040820135926060830135926001600160a01b03608082013581169360a083013590911692909160e081019060c00135600160201b81111561089c57600080fd5b8201836020820111156108ae57600080fd5b803590602001918460018302840111600160201b831117156108cf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550611752945050505050565b34801561091c57600080fd5b5061047d6004803603604081101561093357600080fd5b506001600160a01b038135169060200135611951565b34801561095557600080fd5b506102ad6004803603606081101561096c57600080fd5b81359190810190604081016020820135600160201b81111561098d57600080fd5b82018360208201111561099f57600080fd5b803590602001918460018302840111600160201b831117156109c057600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b811115610a1257600080fd5b820183602082011115610a2457600080fd5b803590602001918460018302840111600160201b83111715610a4557600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955061196e945050505050565b348015610a9257600080fd5b50610a9b6119b7565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610ad7578181015183820152602001610abf565b505050509050019250505060405180910390f35b348015610af757600080fd5b5061047d611a9b565b348015610b0c57600080fd5b506102ad60048036036040811015610b2357600080fd5b6001600160a01b038235169190810190604081016020820135600160201b811115610b4d57600080fd5b820183602082011115610b5f57600080fd5b803590602001918460018302840111600160201b83111715610b8057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550611aa1945050505050565b348015610bcd57600080fd5b506102ad6004803603610100811015610be557600080fd5b810190602081018135600160201b811115610bff57600080fd5b820183602082011115610c1157600080fd5b803590602001918460208302840111600160201b83111715610c3257600080fd5b919390928235926001600160a01b03602082013516929190606081019060400135600160201b811115610c6457600080fd5b820183602082011115610c7657600080fd5b803590602001918460018302840111600160201b83111715610c9757600080fd5b91935091506001600160a01b03813581169160208101358216916040820135916060013516611ac4565b348015610ccd57600080fd5b50610cfa60048036036040811015610ce457600080fd5b506001600160a01b038135169060200135611c14565b6040518080602001836001600160a01b03168152602001828103825284818151815260200191508051906020019060200280838360005b83811015610d49578181015183820152602001610d31565b50505050905001935050505060405180910390f35b348015610d6a57600080fd5b506102ad60048036036020811015610d8157600080fd5b5035611db8565b348015610d9457600080fd5b5061047d6004803603610140811015610dac57600080fd5b6001600160a01b0382351691602081013591810190606081016040820135600160201b811115610ddb57600080fd5b820183602082011115610ded57600080fd5b803590602001918460018302840111600160201b83111715610e0e57600080fd5b919350915060ff813516906020810135906040810135906060810135906001600160a01b03608082013581169160a08101359091169060c00135611e52565b348015610e5957600080fd5b506102ad60048036036040811015610e7057600080fd5b506001600160a01b0381358116916020013516611e7f565b348015610e9457600080fd5b506102ad60048036036020811015610eab57600080fd5b50356001600160a01b0316611fab565b348015610ec757600080fd5b506102ad60048036036060811015610ede57600080fd5b506001600160a01b0381358116916020810135821691604090910135166120d0565b348015610f0c57600080fd5b5061047d612307565b348015610f2157600080fd5b506106d66004803603610140811015610f3957600080fd5b6001600160a01b0382351691602081013591810190606081016040820135600160201b811115610f6857600080fd5b820183602082011115610f7a57600080fd5b803590602001918460018302840111600160201b83111715610f9b57600080fd5b919350915060ff813516906020810135906040810135906060810135906001600160a01b03608082013581169160a08101359091169060c0013561230d565b348015610fe657600080fd5b506102ad60048036036020811015610ffd57600080fd5b50356001600160a01b031661244e565b34801561101957600080fd5b5061047d612496565b34801561102e57600080fd5b506102ad6004803603606081101561104557600080fd5b506001600160a01b03813581169160208101359091169060400135612504565b34801561107157600080fd5b506106d661268f565b6110826126b0565b6001600160a01b038216158015906110a457506001600160a01b038216600114155b80156110b957506001600160a01b0382163014155b6110f2576040805162461bcd60e51b8152602060048201526005602482015264475332303360d81b604482015290519081900360640190fd5b6001600160a01b038281166000908152600260205260409020541615611147576040805162461bcd60e51b815260206004820152600560248201526411d4cc8c0d60da1b604482015290519081900360640190fd5b60026020527fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e080546001600160a01b03848116600081815260408082208054949095166001600160a01b03199485161790945560018082528554909316821790945560038054909201909155905190917f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea2691a280600454146111ec576111ec81611692565b5050565b50505050565b600060016001600160a01b0383161480159061122b57506001600160a01b038281166000908152600160205260409020541615155b92915050565b60006001600160a01b03821660011480159061122b5750506001600160a01b0390811660009081526002602052604090205416151590565b4690565b60007fb648d3644f584ed1c2232d53c46d87e693586486ad0d1175f8656013110b714e338686868660405180866001600160a01b03168152602001856001600160a01b03168152602001848152602001806020018360018111156112cd57fe5b8152602001828103825284818151815260200191508051906020019080838360005b838110156113075781810151838201526020016112ef565b50505050905090810190601f1680156113345780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390a1611351858585856126ee565b95945050505050565b6000606061136a8686868661126d565b915060405160203d0181016040523d81523d6000602083013e8091505094509492505050565b606060008260200267ffffffffffffffff811180156113ae57600080fd5b506040519080825280601f01601f1916602001820160405280156113d9576020820181803683370190505b50905060005b838110156113fc57848101546020808302840101526001016113df565b509392505050565b60076020526000908152604090205481565b61141e6126b0565b604080516001600160a01b0383166020820152818152600c818301526b656e61626c654d6f64756c6560a01b606082015290517f14186b8ac9c91f14b0f16f9e886356157442bb899be26513dfe1d4d5929a5bac9181900360800190a16001600160a01b0381161580159061149d57506001600160a01b038116600114155b6114d6576040805162461bcd60e51b8152602060048201526005602482015264475331303160d81b604482015290519081900360640190fd5b604080516001600160a01b0383166020820152818152601f818301527f6d6f64756c6573206973206e6f74206e756c6c206f722073656e74696e656c00606082015290517f14186b8ac9c91f14b0f16f9e886356157442bb899be26513dfe1d4d5929a5bac9181900360800190a16001600160a01b038181166000908152600160205260409020541615611599576040805162461bcd60e51b815260206004820152600560248201526423a998981960d91b604482015290519081900360640190fd5b604080516001600160a01b0383166020820152818152601a818301527f6d6f64756c6573206973206e6f74206164646564207477696365000000000000606082015290517f14186b8ac9c91f14b0f16f9e886356157442bb899be26513dfe1d4d5929a5bac9181900360800190a1600160208190527fcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f80546001600160a01b03848116600081815260408082208054949095166001600160a01b031994851617909455948552835490911681179092555190917fecdf3a3effea5783a3c4c2140e677577666428d44ed9d474a0b3a4c9943f844091a250565b61169a6126b0565b6003548111156116d9576040805162461bcd60e51b8152602060048201526005602482015264475332303160d81b604482015290519081900360640190fd5b6001811015611717576040805162461bcd60e51b815260206004820152600560248201526423a999181960d91b604482015290519081900360640190fd5b60048190556040805182815290517f610f7ff2b304ae8903c3de74c60c6ab1f7d6226b3f52c5161905bb5ad4039c939181900360200190a150565b600060606005543360045460405160200180848152602001836001600160a01b03168152602001828152602001935050505060405160208183030381529060405290507f66753cd2356569ee081232e3be8909b950e0a76c1f8460c3a5e3c2be32b11bed8d8d8d8d8d8d8d8d8d8d8d8c604051808d6001600160a01b031681526020018c8152602001806020018a60018111156117eb57fe5b8152602001898152602001888152602001878152602001866001600160a01b03168152602001856001600160a01b03168152602001806020018060200184810384528e8e828181526020019250808284376000838201819052601f909101601f19169092018681038552885181528851602091820193918a019250908190849084905b8381101561188657818101518382015260200161186e565b50505050905090810190601f1680156118b35780820380516001836020036101000a031916815260200191505b50848103825285518152855160209182019187019080838360005b838110156118e65781810151838201526020016118ce565b50505050905090810190601f1680156119135780820380516001836020036101000a031916815260200191505b509f5050505050505050505050505050505060405180910390a16119408d8d8d8d8d8d8d8d8d8d8d6127cc565b9d9c50505050505050505050505050565b600860209081526000928352604080842090915290825290205481565b600454806119ab576040805162461bcd60e51b8152602060048201526005602482015264475330303160d81b604482015290519081900360640190fd5b6111f0848484846111f0565b6060600060035467ffffffffffffffff811180156119d457600080fd5b506040519080825280602002602001820160405280156119fe578160200160208202803683370190505b506001600090815260026020527fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e054919250906001600160a01b03165b6001600160a01b038116600114611a935780838381518110611a5957fe5b6001600160a01b03928316602091820292909201810191909152918116600090815260029092526040909120546001929092019116611a3b565b509091505090565b60055481565b600080825160208401855af480600052503d6020523d600060403e60403d016000fd5b611b028a8a808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250612bc0915050565b6001600160a01b03841615611b1a57611b1a84612df7565b611b5a8787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612e6192505050565b8115611b7157611b6f82600060018685612fa9565b505b336001600160a01b03167f141df868a6331af528e38c83b7aa03edc19be66e37ae67f9285bf4f8e3c6a1a88b8b8b8b896040518080602001858152602001846001600160a01b03168152602001836001600160a01b031681526020018281038252878782818152602001925060200280828437600083820152604051601f909101601f19169092018290039850909650505050505050a250505050505050505050565b606060006001600160a01b03841660011480611c345750611c34846111f6565b611c6d576040805162461bcd60e51b8152602060048201526005602482015264475331303560d81b604482015290519081900360640190fd5b60008311611caa576040805162461bcd60e51b815260206004820152600560248201526423a998981b60d91b604482015290519081900360640190fd5b8267ffffffffffffffff81118015611cc157600080fd5b50604051908082528060200260200182016040528015611ceb578160200160208202803683370190505b506001600160a01b03808616600090815260016020526040812054929450911691505b6001600160a01b03821615801590611d3057506001600160a01b038216600114155b8015611d3b57508381105b15611d825781838281518110611d4d57fe5b6001600160a01b0392831660209182029290920181019190915292811660009081526001938490526040902054169101611d0e565b6001600160a01b038216600114611dad57826001820381518110611da257fe5b602002602001015191505b808352509250929050565b336000908152600260205260409020546001600160a01b0316611e0a576040805162461bcd60e51b8152602060048201526005602482015264047533033360dc1b604482015290519081900360640190fd5b336000818152600860209081526040808320858452909152808220600190555183917ff2a0eb156472d1440255b0d7c1e19cc07115d1051fe605b0dce69acfec884d9c91a350565b6000611e678c8c8c8c8c8c8c8c8c8c8c61230d565b8051906020012090509b9a5050505050505050505050565b611e876126b0565b6001600160a01b03811615801590611ea957506001600160a01b038116600114155b611ee2576040805162461bcd60e51b8152602060048201526005602482015264475331303160d81b604482015290519081900360640190fd5b6001600160a01b03828116600090815260016020526040902054811690821614611f3b576040805162461bcd60e51b8152602060048201526005602482015264475331303360d81b604482015290519081900360640190fd5b6001600160a01b03818116600081815260016020526040808220805487861684528284208054919096166001600160a01b0319918216179095558383528054909416909355915190917faab4fa2b463f581b2b32cb3b7e3b704b9ce37cc209b5fb4d77e593ace405427691a25050565b611fb36126b0565b6001600160a01b0381161561207457604080516301ffc9a760e01b815263736bd41d60e11b600482015290516001600160a01b038316916301ffc9a7916024808301926020929190829003018186803b15801561200f57600080fd5b505afa158015612023573d6000803e3d6000fd5b505050506040513d602081101561203957600080fd5b5051612074576040805162461bcd60e51b8152602060048201526005602482015264047533330360dc1b604482015290519081900360640190fd5b7f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c88181556040516001600160a01b038316907f1151116914515bc0891ff9047a6cb32cf902546f83066499bcf8ba33d2353fa290600090a25050565b6120d86126b0565b6001600160a01b038116158015906120fa57506001600160a01b038116600114155b801561210f57506001600160a01b0381163014155b612148576040805162461bcd60e51b8152602060048201526005602482015264475332303360d81b604482015290519081900360640190fd5b6001600160a01b03818116600090815260026020526040902054161561219d576040805162461bcd60e51b815260206004820152600560248201526411d4cc8c0d60da1b604482015290519081900360640190fd5b6001600160a01b038216158015906121bf57506001600160a01b038216600114155b6121f8576040805162461bcd60e51b8152602060048201526005602482015264475332303360d81b604482015290519081900360640190fd5b6001600160a01b03838116600090815260026020526040902054811690831614612251576040805162461bcd60e51b8152602060048201526005602482015264475332303560d81b604482015290519081900360640190fd5b6001600160a01b03828116600081815260026020526040808220805486861680855283852080549288166001600160a01b03199384161790559589168452828420805482169096179095558383528054909416909355915190917ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf91a26040516001600160a01b038216907f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea2690600090a2505050565b60045490565b606060007fbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d860001b8d8d8d8d60405180838380828437808301925050509250505060405180910390208c8c8c8c8c8c8c604051602001808c81526020018b6001600160a01b031681526020018a815260200189815260200188600181111561239157fe5b8152602001878152602001868152602001858152602001846001600160a01b03168152602001836001600160a01b031681526020018281526020019b505050505050505050505050604051602081830303815290604052805190602001209050601960f81b600160f81b612403612496565b604080516001600160f81b0319948516602082015292909316602183015260228201526042808201939093528151808203909301835260620190529c9b505050505050505050505050565b6124566126b0565b61245f81612df7565b6040516001600160a01b038216907f5ac6c46c93c8d0e53714ba3b53db3e7c046da994313d7ed0d192028bc7c228b090600090a250565b60007f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a794692186124c1611269565b3060405160200180848152602001838152602001826001600160a01b03168152602001935050505060405160208183030381529060405280519060200120905090565b61250c6126b0565b80600160035403101561254e576040805162461bcd60e51b8152602060048201526005602482015264475332303160d81b604482015290519081900360640190fd5b6001600160a01b0382161580159061257057506001600160a01b038216600114155b6125a9576040805162461bcd60e51b8152602060048201526005602482015264475332303360d81b604482015290519081900360640190fd5b6001600160a01b03838116600090815260026020526040902054811690831614612602576040805162461bcd60e51b8152602060048201526005602482015264475332303560d81b604482015290519081900360640190fd5b6001600160a01b03828116600081815260026020526040808220805488861684528284208054919096166001600160a01b031991821617909555838352805490941690935560038054600019019055915190917ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf91a2806004541461268a5761268a81611692565b505050565b60405180604001604052806005815260200164312e342e3160d81b81525081565b3330146126ec576040805162461bcd60e51b8152602060048201526005602482015264475330333160d81b604482015290519081900360640190fd5b565b6000336001148015906127185750336000908152600160205260409020546001600160a01b031615155b612751576040805162461bcd60e51b815260206004820152600560248201526411d4cc4c0d60da1b604482015290519081900360640190fd5b612760858585856000196130b9565b905080156127985760405133907f6895c13664aa4f67288b25d7a21d7aaa34916e355fb9b6fae0a139a9085becb890600090a26127c4565b60405133907facd2c8702804128fdb0db2bb49f6d127dd0181c13fd45dbfe16de0930e2bd37590600090a25b949350505050565b60008060006127e68e8e8e8e8e8e8e8e8e8e60055461230d565b600580546001019055805160208201209250905061280582828661196e565b5060006128106130f9565b90506001600160a01b0381161561298b57806001600160a01b03166375f0bb528f8f8f8f8f8f8f8f8f8f8f336040518d63ffffffff1660e01b8152600401808d6001600160a01b031681526020018c8152602001806020018a600181111561287457fe5b8152602001898152602001888152602001878152602001866001600160a01b03168152602001856001600160a01b0316815260200180602001846001600160a01b0316815260200183810383528d8d828181526020019250808284376000838201819052601f909101601f191690920185810384528751815287516020918201939189019250908190849084905b8381101561291a578181015183820152602001612902565b50505050905090810190601f1680156129475780820380516001836020036101000a031916815260200191505b509e505050505050505050505050505050600060405180830381600087803b15801561297257600080fd5b505af1158015612986573d6000803e3d6000fd5b505050505b61299f603f60408b02046109c48b0161311e565b6101f4015a10156129df576040805162461bcd60e51b8152602060048201526005602482015264047533031360dc1b604482015290519081900360640190fd5b60005a9050612a488f8f8f8f8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508e8c600014612a3d578e612a43565b6109c45a035b6130b9565b9350612a555a8290613137565b90508380612a6257508915155b80612a6c57508715155b612aa5576040805162461bcd60e51b8152602060048201526005602482015264475330313360d81b604482015290519081900360640190fd5b60008815612abd57612aba828b8b8b8b612fa9565b90505b8415612afe5760408051828152905185917f442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e919081900360200190a2612b35565b60408051828152905185917f23428b18acfb3ea64b08dc0c1d296ea9c09702c09083ca5272e64d115b687d23919081900360200190a25b50506001600160a01b03811615612baf57806001600160a01b0316639327136883856040518363ffffffff1660e01b815260040180838152602001821515815260200192505050600060405180830381600087803b158015612b9657600080fd5b505af1158015612baa573d6000803e3d6000fd5b505050505b50509b9a5050505050505050505050565b60045415612bfd576040805162461bcd60e51b8152602060048201526005602482015264047533230360dc1b604482015290519081900360640190fd5b8151811115612c3b576040805162461bcd60e51b8152602060048201526005602482015264475332303160d81b604482015290519081900360640190fd5b6001811015612c79576040805162461bcd60e51b815260206004820152600560248201526423a999181960d91b604482015290519081900360640190fd5b600160005b8351811015612dc4576000848281518110612c9557fe5b6020026020010151905060006001600160a01b0316816001600160a01b031614158015612ccc57506001600160a01b038116600114155b8015612ce157506001600160a01b0381163014155b8015612cff5750806001600160a01b0316836001600160a01b031614155b612d38576040805162461bcd60e51b8152602060048201526005602482015264475332303360d81b604482015290519081900360640190fd5b6001600160a01b038181166000908152600260205260409020541615612d8d576040805162461bcd60e51b815260206004820152600560248201526411d4cc8c0d60da1b604482015290519081900360640190fd5b6001600160a01b03928316600090815260026020526040902080546001600160a01b03191693821693909317909255600101612c7e565b506001600160a01b0316600090815260026020526040902080546001600160a01b03191660011790559051600355600455565b6001600160a01b038116301415612e3d576040805162461bcd60e51b8152602060048201526005602482015264047533430360dc1b604482015290519081900360640190fd5b7f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d555565b600160008190526020527fcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f546001600160a01b031615612ed0576040805162461bcd60e51b8152602060048201526005602482015264047533130360dc1b604482015290519081900360640190fd5b6001600081905260208190527fcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f80546001600160a01b03191690911790556001600160a01b038216156111ec57612f268261314c565b612f5f576040805162461bcd60e51b815260206004820152600560248201526423a998181960d91b604482015290519081900360640190fd5b612f708260008360016000196130b9565b6111ec576040805162461bcd60e51b8152602060048201526005602482015264047533030360dc1b604482015290519081900360640190fd5b6000806001600160a01b03831615612fc15782612fc3565b325b90506001600160a01b03841661305b57612ff53a8610612fe3573a612fe5565b855b612fef8989613152565b90613164565b6040519092506001600160a01b0382169083156108fc029084906000818181858888f19350505050613056576040805162461bcd60e51b8152602060048201526005602482015264475330313160d81b604482015290519081900360640190fd5b6130af565b61306985612fef8989613152565b915061307684828461318b565b6130af576040805162461bcd60e51b815260206004820152600560248201526423a998189960d91b604482015290519081900360640190fd5b5095945050505050565b600060018360018111156130c957fe5b14156130e2576000808551602087018986f49050611351565b600080855160208701888a87f19695505050505050565b7f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c85490565b60008183101561312e5781613130565b825b9392505050565b60008282111561314657600080fd5b50900390565b3b151590565b60008282018381101561313057600080fd5b6000826131735750600061122b565b8282028284828161318057fe5b041461313057600080fd5b604080516001600160a01b03841660248201526044808201849052825180830390910181526064909101909152602081810180516001600160e01b031663a9059cbb60e01b1781528251600093929184919082896127105a03f13d80156131fd57602081146132055760009350613210565b819350613210565b600051158215171593505b505050939250505056fea2646970667358221220a051bb4d5093d394d74cc24320a5a5c8b5a59d32a3f8766cfc0fc65bf0f0f9e364736f6c63430007060033"; }