From ac03f42aedb9dae41eec6860caf46aff4814edbf Mon Sep 17 00:00:00 2001 From: Mikko Ohtamaa Date: Tue, 31 Dec 2024 11:55:50 +0100 Subject: [PATCH] Add Gnosis Safe module integration for Trading Strategy (#256) - Integrate `TradingStrategyModuleV0` module to Gnosis Safe-based protocols using Zodiac module - Mainly needed for Lagoon vaults, but can work for others: vanilla Safe, DAOs --- .gitmodules | 12 + CHANGELOG.md | 2 + Makefile | 14 +- contracts/guard/remappings.txt | 2 + contracts/guard/script/Counter.s.sol | 12 - contracts/guard/src/GuardV0.sol | 477 +---------------- contracts/guard/src/GuardV0Base.sol | 502 ++++++++++++++++++ contracts/guard/src/IGuard.sol | 11 +- contracts/guard/src/MockGuard.sol | 2 +- contracts/safe-integration/.gitignore | 14 + contracts/safe-integration/README.md | 10 + contracts/safe-integration/foundry.toml | 9 + .../lib/openzeppelin-contracts | 1 + .../lib/openzeppelin-contracts-upgradeable | 1 + contracts/safe-integration/lib/safe-contracts | 1 + contracts/safe-integration/lib/zodiac | 1 + contracts/safe-integration/remappings.txt | 4 + contracts/safe-integration/src/MockSafe.sol | 43 ++ .../src/TradingStrategyModuleV0.sol | 101 ++++ eth_defi/abi/safe-integration/MockSafe.json | 1 + .../TradingStrategyModuleV0.json | 1 + tests/lagoon/conftest.py | 1 + tests/safe-integration/test_guard_safe_e2e.py | 380 +++++++++++++ .../test_guard_safe_uniswap_v2.py | 277 ++++++++++ 24 files changed, 1396 insertions(+), 483 deletions(-) delete mode 100644 contracts/guard/script/Counter.s.sol create mode 100644 contracts/guard/src/GuardV0Base.sol create mode 100644 contracts/safe-integration/.gitignore create mode 100644 contracts/safe-integration/README.md create mode 100644 contracts/safe-integration/foundry.toml create mode 160000 contracts/safe-integration/lib/openzeppelin-contracts create mode 160000 contracts/safe-integration/lib/openzeppelin-contracts-upgradeable create mode 160000 contracts/safe-integration/lib/safe-contracts create mode 160000 contracts/safe-integration/lib/zodiac create mode 100644 contracts/safe-integration/remappings.txt create mode 100644 contracts/safe-integration/src/MockSafe.sol create mode 100644 contracts/safe-integration/src/TradingStrategyModuleV0.sol create mode 100644 eth_defi/abi/safe-integration/MockSafe.json create mode 100644 eth_defi/abi/safe-integration/TradingStrategyModuleV0.json create mode 100644 tests/safe-integration/test_guard_safe_e2e.py create mode 100644 tests/safe-integration/test_guard_safe_uniswap_v2.py diff --git a/.gitmodules b/.gitmodules index 619f6b21..3eaff417 100644 --- a/.gitmodules +++ b/.gitmodules @@ -44,3 +44,15 @@ [submodule "contracts/velvet-core"] path = contracts/velvet-core url = https://github.com/Velvet-Capital/velvet-core.git +[submodule "contracts/safe-integration/lib/zodiac"] + path = contracts/safe-integration/lib/zodiac + url = https://github.com/gnosisguild/zodiac.git +[submodule "contracts/safe-integration/lib/safe-contracts"] + path = contracts/safe-integration/lib/safe-contracts + url = git@github.com:safe-global/safe-smart-account.git +[submodule "contracts/safe-integration/lib/openzeppelin-contracts"] + path = contracts/safe-integration/lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts.git +[submodule "contracts/safe-integration/lib/openzeppelin-contracts-upgradeable"] + path = contracts/safe-integration/lib/openzeppelin-contracts-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable.git diff --git a/CHANGELOG.md b/CHANGELOG.md index 31ec116b..c683c432 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ # Current +- Mainly needed for Lagoon vaults, but can work for others: vanilla Safe, DAOs - Add Multicall3 support in `multicall_batcher` module - Add `SwapRouter02` support on Base for doing Uniswap v3 swaps - Add Uniswap V3 quoter for the valuation - Add `buy_tokens()` helper to buy multiple tokens once, automatically look up best routes - Fix: Base MEV protected broadcast failed +- Add: Integrate `TradingStrategyModuleV0` module to Gnosis Safe-based protocols using Zodiac module. Mainly needed for Lagoon vaults, but can work for others: vanilla Safe, DAOs. # 0.27 diff --git a/Makefile b/Makefile index 384227e0..3275488d 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,18 @@ guard: \) \ -exec cp {} eth_defi/abi/guard \; +# Guard as a safe module +safe-integration: + @mkdir -p eth_defi/abi/safe-integration + @(cd contracts/safe-integration && forge build) + @find contracts/safe-integration/out \ + \( \ + -name "TradingStrategyModuleV0.json" \ + -o \ + -name "MockSafe.json" \ + \) \ + -exec cp {} eth_defi/abi/safe-integration \; + # Terms of service acceptance manager contract terms-of-service: @mkdir -p eth_defi/abi/terms-of-service @@ -144,7 +156,7 @@ clean-abi: # Compile all contracts we are using # # Move ABI files to within a Python package for PyPi distribution -compile-projects-and-prepare-abi: clean-abi sushi in-house guard copy-uniswapv3-abi aavev3 enzyme dhedge centre 1delta +compile-projects-and-prepare-abi: clean-abi sushi in-house guard safe-integration copy-uniswapv3-abi aavev3 enzyme dhedge centre 1delta all: clean-docs compile-projects-and-prepare-abi build-docs diff --git a/contracts/guard/remappings.txt b/contracts/guard/remappings.txt index 4c90c598..af20721f 100644 --- a/contracts/guard/remappings.txt +++ b/contracts/guard/remappings.txt @@ -1 +1,3 @@ @openzeppelin/=lib/openzeppelin-contracts/contracts +@gnosis.pm/=lib + diff --git a/contracts/guard/script/Counter.s.sol b/contracts/guard/script/Counter.s.sol deleted file mode 100644 index 1a47b40b..00000000 --- a/contracts/guard/script/Counter.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console2} from "forge-std/Script.sol"; - -contract CounterScript is Script { - function setUp() public {} - - function run() public { - vm.broadcast(); - } -} diff --git a/contracts/guard/src/GuardV0.sol b/contracts/guard/src/GuardV0.sol index 9013f0b6..44c38746 100644 --- a/contracts/guard/src/GuardV0.sol +++ b/contracts/guard/src/GuardV0.sol @@ -8,6 +8,7 @@ pragma solidity ^0.8.0; import "@openzeppelin/access/Ownable.sol"; import "./lib/Path.sol"; import "./IGuard.sol"; +import "./GuardV0Base.sol"; /** * Prototype guard implementation. @@ -15,484 +16,24 @@ import "./IGuard.sol"; * - Hardcoded actions for Uniswap v2, v3, 1delta * */ -contract GuardV0 is IGuard, Ownable { - using Path for bytes; - using BytesLib for bytes; - - /** - * Constants for 1delta path decoding using similar approach as Uniswap v3 `Path.sol` - * - * Check our implementation at: `validate1deltaPath()` - */ - /// @dev The length of the bytes encoded address - uint256 private constant ADDR_SIZE = 20; - /// @dev The length of the bytes encoded pool fee - uint256 private constant ONEDELTA_FEE_SIZE = 3; - /// @dev The length of the bytes encoded DEX ID - uint256 private constant ONEDELTA_PID_SIZE = 1; - /// @dev The length of the bytes encoded action - uint256 private constant ONEDELTA_ACTION_SIZE = 1; - /// @dev The offset of a single token address, fee, pid and action - uint256 private constant ONEDELTA_NEXT_OFFSET = ADDR_SIZE + ONEDELTA_FEE_SIZE + ONEDELTA_PID_SIZE + ONEDELTA_ACTION_SIZE; - /// @dev The offset of an encoded pool key - uint256 private constant ONEDELTA_POP_OFFSET = ONEDELTA_NEXT_OFFSET + ADDR_SIZE; - /// @dev The minimum length of an encoding that contains 2 or more pools - uint256 private constant ONEDELTA_MULTIPLE_POOLS_MIN_LENGTH = ONEDELTA_POP_OFFSET + ONEDELTA_NEXT_OFFSET; - - struct ExactInputParams { - bytes path; - address recipient; - uint256 deadline; - uint256 amountIn; - uint256 amountOutMinimum; - } - - struct ExactOutputParams { - bytes path; - address recipient; - uint256 deadline; - uint256 amountOut; - uint256 amountInMaximum; - } - - // Allowed ERC20.approve() - mapping(address target => mapping(bytes4 selector => bool allowed)) public allowedCallSites; - - // How many call sites we have enabled all-time counter. - // - // Used for diagnostics/debugging. - // - uint public callSiteCount; - - // Allowed ERC-20 tokens we may receive or send in a trade - mapping(address token => bool allowed) public allowedAssets; - - // Allowed trade executor hot wallets - mapping(address sender => bool allowed) public allowedSenders; - - // Allowed token receivers post trade - mapping(address receiver => bool allowed) public allowedReceivers; - - // Allowed owners - mapping(address destination => bool allowed) public allowedWithdrawDestinations; - - // Allowed routers - mapping(address destination => bool allowed) public allowedApprovalDestinations; - - // Allowed routers - mapping(address destination => bool allowed) public allowedDelegationApprovalDestinations; - - event CallSiteApproved(address target, bytes4 selector, string notes); - event CallSiteRemoved(address target, bytes4 selector, string notes); - - event SenderApproved(address sender, string notes); - event SenderRemoved(address sender, string notes); - - event ReceiverApproved(address sender, string notes); - event ReceiverRemoved(address sender, string notes); - - event WithdrawDestinationApproved(address sender, string notes); - event WithdrawDestinationRemoved(address sender, string notes); - - event ApprovalDestinationApproved(address sender, string notes); - event ApprovalDestinationRemoved(address sender, string notes); - - event DelegationApprovalDestinationApproved(address sender, string notes); - event DelegationApprovalDestinationRemoved(address sender, string notes); - - event AssetApproved(address sender, string notes); - event AssetRemoved(address sender, string notes); +contract GuardV0 is GuardV0Base, Ownable { constructor() Ownable() { } - function getSelector(string memory _func) internal pure returns (bytes4) { - // https://solidity-by-example.org/function-selector/ - return bytes4(keccak256(bytes(_func))); - } - /** - * Get the address of the proto DAO + * Specify a modifier for guard owner */ - function getGovernanceAddress() public view returns (address) { - return owner(); + modifier onlyGuardOwner() override { + require(owner() == _msgSender(), "Ownable: caller is not the owner"); + _; } /** - * Track version during internal development. - * - * We bump up when new whitelistings added. - */ - function getInternalVersion() public pure returns (uint8) { - return 1; - } - - function allowCallSite(address target, bytes4 selector, string calldata notes) public onlyOwner { - allowedCallSites[target][selector] = true; - callSiteCount++; - emit CallSiteApproved(target, selector, notes); - } - - function removeCallSite(address target, bytes4 selector, string calldata notes) public onlyOwner { - delete allowedCallSites[target][selector]; - emit CallSiteRemoved(target, selector, notes); - } - - function allowSender(address sender, string calldata notes) public onlyOwner { - allowedSenders[sender] = true; - emit SenderApproved(sender, notes); - } - - function removeSender(address sender, string calldata notes) public onlyOwner { - delete allowedSenders[sender]; - emit SenderRemoved(sender, notes); - } - - function allowReceiver(address receiver, string calldata notes) public onlyOwner { - allowedReceivers[receiver] = true; - emit ReceiverApproved(receiver, notes); - } - - function removeReceiver(address receiver, string calldata notes) public onlyOwner { - delete allowedReceivers[receiver]; - emit ReceiverRemoved(receiver, notes); - } - - function allowWithdrawDestination(address destination, string calldata notes) public onlyOwner { - allowedWithdrawDestinations[destination] = true; - emit WithdrawDestinationApproved(destination, notes); - } - - function removeWithdrawDestination(address destination, string calldata notes) public onlyOwner { - delete allowedWithdrawDestinations[destination]; - emit WithdrawDestinationRemoved(destination, notes); - } - - function allowApprovalDestination(address destination, string calldata notes) public onlyOwner { - allowedApprovalDestinations[destination] = true; - emit ApprovalDestinationApproved(destination, notes); - } - - function removeApprovalDestination(address destination, string calldata notes) public onlyOwner { - delete allowedApprovalDestinations[destination]; - emit ApprovalDestinationRemoved(destination, notes); - } - - function allowDelegationApprovalDestination(address destination, string calldata notes) public onlyOwner { - allowedDelegationApprovalDestinations[destination] = true; - emit ApprovalDestinationApproved(destination, notes); - } - - function removeDelegationApprovalDestination(address destination, string calldata notes) public onlyOwner { - delete allowedApprovalDestinations[destination]; - emit ApprovalDestinationRemoved(destination, notes); - } - - function allowAsset(address asset, string calldata notes) public onlyOwner { - allowedAssets[asset] = true; - emit AssetApproved(asset, notes); - } - - function removeAsset(address asset, string calldata notes) public onlyOwner { - delete allowedAssets[asset]; - emit AssetRemoved(asset, notes); - } - - // Basic check if any target contract is whitelisted - function isAllowedCallSite(address target, bytes4 selector) public view returns (bool) { - return allowedCallSites[target][selector]; - } - - function isAllowedSender(address sender) public view returns (bool) { - return allowedSenders[sender] == true; - } - - // Assume any tokens are send back to the vault - function isAllowedReceiver(address receiver) public view returns (bool) { - return allowedReceivers[receiver] == true; - } - - function isAllowedWithdrawDestination(address receiver) public view returns (bool) { - return allowedWithdrawDestinations[receiver] == true; - } - - function isAllowedApprovalDestination(address receiver) public view returns (bool) { - return allowedApprovalDestinations[receiver] == true; - } - - function isAllowedDelegationApprovalDestination(address receiver) public view returns (bool) { - return allowedDelegationApprovalDestinations[receiver] == true; - } - - function isAllowedAsset(address token) public view returns (bool) { - return allowedAssets[token] == true; - } - - function validate_transfer(bytes memory callData) public view { - (address to, ) = abi.decode(callData, (address, uint)); - require(isAllowedWithdrawDestination(to), "validate_transfer: Receiver address not whitelisted by Guard"); - } - - function validate_approve(bytes memory callData) public view { - (address to, ) = abi.decode(callData, (address, uint)); - require(isAllowedApprovalDestination(to), "validate_approve: Approve address does not match"); - } - - function validate_approveDelegation(bytes memory callData) public view { - (address to, ) = abi.decode(callData, (address, uint)); - require(isAllowedDelegationApprovalDestination(to), "validate_approveDelegation: Approve delegation address does not match"); - } - - function whitelistToken(address token, string calldata notes) external { - allowCallSite(token, getSelector("transfer(address,uint256)"), notes); - allowCallSite(token, getSelector("approve(address,uint256)"), notes); - allowAsset(token, notes); - } - - function whitelistTokenForDelegation(address token, string calldata notes) external { - allowCallSite(token, getSelector("approveDelegation(address,uint256)"), notes); - allowAsset(token, notes); - } - - function validateCall( - address sender, - address target, - bytes calldata callDataWithSelector - ) external view { - - if(sender == getGovernanceAddress()) { - // Governance can manually recover any issue - return; - } - - require(isAllowedSender(sender), "validateCall: Sender not allowed"); - - // Assume sender is trade-executor hot wallet - - bytes4 selector = bytes4(callDataWithSelector[:4]); - bytes calldata callData = callDataWithSelector[4:]; - require(isAllowedCallSite(target, selector), "validateCall: Call site not allowed"); - - if(selector == getSelector("swapExactTokensForTokens(uint256,uint256,address[],address,uint256)")) { - validate_swapExactTokensForTokens(callData); - } else if(selector == getSelector("exactInput((bytes,address,uint256,uint256,uint256))")) { - validate_exactInput(callData); - } else if(selector == getSelector("multicall(bytes[])")) { - validate_1deltaMulticall(callData); - } else if(selector == getSelector("transfer(address,uint256)")) { - validate_transfer(callData); - } else if(selector == getSelector("approve(address,uint256)")) { - validate_approve(callData); - } else if(selector == getSelector("approveDelegation(address,uint256)")) { - validate_approveDelegation(callData); - } else if(selector == getSelector("supply(address,uint256,address,uint16)")) { - validate_aaveSupply(callData); - } else if(selector == getSelector("withdraw(address,uint256,address)")) { - validate_aaveWithdraw(callData); - } else { - revert("Unknown function selector"); - } - } - - // Validate Uniswap v2 trade - function validate_swapExactTokensForTokens(bytes memory callData) public view { - (, , address[] memory path, address to, ) = abi.decode(callData, (uint, uint, address[], address, uint)); - - require(isAllowedReceiver(to), "validate_swapExactTokensForTokens: Receiver address not whitelisted by Guard"); - - address token; - for (uint256 i = 0; i < path.length; i++) { - token = path[i]; - require(isAllowedAsset(token), "Token not allowed"); - } - } - - function whitelistUniswapV2Router(address router, string calldata notes) external { - allowCallSite(router, getSelector("swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"), notes); - allowApprovalDestination(router, notes); - } - - // validate Uniswap v3 trade - function validate_exactInput(bytes memory callData) public view { - (ExactInputParams memory params) = abi.decode(callData, (ExactInputParams)); - - require(isAllowedReceiver(params.recipient), "validate_exactInput: Receiver address not whitelisted by Guard"); - validateUniswapV3Path(params.path); - } - - function validate_exactOutput(bytes memory callData) public view { - (ExactOutputParams memory params) = abi.decode(callData, (ExactOutputParams)); - - require(isAllowedReceiver(params.recipient), "validate_exactOutput: Receiver address not whitelisted by Guard"); - validateUniswapV3Path(params.path); - } - - function validateUniswapV3Path(bytes memory path) public view { - address tokenIn; - address tokenOut; - - while (true) { - (tokenOut, tokenIn, ) = path.decodeFirstPool(); - - require(isAllowedAsset(tokenIn), "validateUniswapV3Path: Token not allowed"); - require(isAllowedAsset(tokenOut), "validateUniswapV3Path: Token not allowed"); - - if (path.hasMultiplePools()) { - path = path.skipToken(); - } else { - break; - } - } - } - - function whitelistUniswapV3Router(address router, string calldata notes) external { - allowCallSite(router, getSelector("exactInput((bytes,address,uint256,uint256,uint256))"), notes); - allowCallSite(router, getSelector("exactOutput((bytes,address,uint256,uint256,uint256))"), notes); - allowApprovalDestination(router, notes); - } - - // validate 1delta trade - function validate_1deltaMulticall(bytes memory callData) public view { - (bytes[] memory callArr) = abi.decode(callData, (bytes[])); - - // loop through all sub-calls and validate - for (uint i; i < callArr.length; i++) { - bytes memory callDataWithSelector = callArr[i]; - - // bytes memory has to be sliced using BytesLib - bytes4 selector = bytes4(callDataWithSelector.slice(0, 4)); - bytes memory subCallData = callDataWithSelector.slice(4, callDataWithSelector.length - 4); - - // validate each sub-call - if (selector == getSelector("transferERC20In(address,uint256)")) { - validate_transferERC20In(subCallData); - } else if (selector == getSelector("transferERC20AllIn(address)")) { - validate_transferERC20AllIn(subCallData); - } else if (selector == getSelector("deposit(address,address)")) { - validate_1deltaDeposit(subCallData); - } else if (selector == getSelector("withdraw(address,address)")) { - validate_1deltaWithdraw(subCallData); - } else if (selector == getSelector("flashSwapExactIn(uint256,uint256,bytes)")) { - validate_flashSwapExactInt(subCallData); - } else if (selector == getSelector("flashSwapExactOut(uint256,uint256,bytes)")) { - validate_flashSwapExactOut(subCallData); - } else if (selector == getSelector("flashSwapAllOut(uint256,bytes)")) { - validate_flashSwapAllOut(subCallData); - } else { - revert("validate_1deltaMulticall: Unknown function selector"); - } - } - } - - // 1delta implementation: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/FlashAggregator.sol#L78-L81 - function validate_transferERC20In(bytes memory callData) public view { - (address token, ) = abi.decode(callData, (address, uint256)); - - require(isAllowedAsset(token), "validate_transferERC20In: Token not allowed"); - } - - // 1delta implementation: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/FlashAggregator.sol#L83-L93 - function validate_transferERC20AllIn(bytes memory callData) public view { - (address token) = abi.decode(callData, (address)); - - require(isAllowedAsset(token), "validate_transferERC20AllIn: Token not allowed"); - } - - // 1delta implementation: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/FlashAggregator.sol#L34-L39 - function validate_1deltaDeposit(bytes memory callData) public view { - (address token, address receiver) = abi.decode(callData, (address, address)); - - require(isAllowedAsset(token), "validate_transferERC20AllIn: Token not allowed"); - require(isAllowedReceiver(receiver), "validate_deposit: Receiver address not whitelisted by Guard"); - } - - // 1delta: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/FlashAggregator.sol#L71-L74 - function validate_1deltaWithdraw(bytes memory callData) public view { - (address token, address receiver) = abi.decode(callData, (address, address)); - - require(isAllowedAsset(token), "validate_withdraw: Token not allowed"); - require(isAllowedReceiver(receiver), "validate_deposit: Receiver address not whitelisted by Guard"); - } - - // 1delta implementation: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/MarginTrading.sol#L43-L89 - function validate_flashSwapExactInt(bytes memory callData) public view { - (, , bytes memory path) = abi.decode(callData, (uint256, uint256, bytes)); - - validate1deltaPath(path); - } - - // Reference in 1delta: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/MarginTrading.sol#L91-L103 - function validate_flashSwapExactOut(bytes memory callData) public view { - (, , bytes memory path) = abi.decode(callData, (uint256, uint256, bytes)); - - validate1deltaPath(path); - } - - // 1delta implementation: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/MarginTrading.sol#L153-L203 - function validate_flashSwapAllOut(bytes memory callData) public view { - (, bytes memory path) = abi.decode(callData, (uint256, bytes)); - - validate1deltaPath(path); - } - - /** - * Our implementation of 1delta path decoding and validation using similar - * approach as Uniswap v3 `Path.sol` - * - * Read more: - * - How 1delta encodes the path: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/test-ts/1delta/shared/aggregatorPath.ts#L5-L32 - * - How 1delta decodes the path: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/MarginTrading.sol#L54-L60 + * Get the address of the proto DAO */ - function validate1deltaPath(bytes memory path) public view { - address tokenIn; - address tokenOut; - - while (true) { - tokenIn = path.toAddress(0); - tokenOut = path.toAddress(ONEDELTA_NEXT_OFFSET); - - require(isAllowedAsset(tokenIn), "validate1deltaPath: Token not allowed"); - require(isAllowedAsset(tokenOut), "validate1deltaPath: Token not allowed"); - - // iterate to next slice if the path still contains multiple pools - if (path.length >= ONEDELTA_MULTIPLE_POOLS_MIN_LENGTH) { - path = path.slice(ONEDELTA_NEXT_OFFSET, path.length - ONEDELTA_NEXT_OFFSET); - } else { - break; - } - } - } - - function whitelistOnedelta(address brokerProxy, address lendingPool, string calldata notes) external { - allowCallSite(brokerProxy, getSelector("multicall(bytes[])"), notes); - allowApprovalDestination(brokerProxy, notes); - allowApprovalDestination(lendingPool, notes); - - // vToken has to be approved delegation for broker proxy - // Reference in 1delta tests: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/test-ts/1delta/aave/marginSwap.spec.ts#L206 - allowDelegationApprovalDestination(brokerProxy, notes); - } - - // Aave V3 implementation: https://github.com/aave/aave-v3-core/blob/e0bfed13240adeb7f05cb6cbe5e7ce78657f0621/contracts/protocol/pool/Pool.sol#L145 - function validate_aaveSupply(bytes memory callData) public view { - (address token, , , ) = abi.decode(callData, (address, uint, address, uint)); - - require(isAllowedAsset(token), "Token not allowed"); - // require(isAllowedReceiver(wallet), "Receiver address not whitelisted by Guard"); - } - - // Aave V3 implementation: https://github.com/aave/aave-v3-core/blob/e0bfed13240adeb7f05cb6cbe5e7ce78657f0621/contracts/protocol/pool/Pool.sol#L198 - function validate_aaveWithdraw(bytes memory callData) public view { - (address token, , address to) = abi.decode(callData, (address, uint, address)); - - require(isAllowedAsset(token), "Token not allowed"); - require(isAllowedReceiver(to), "Receiver address not whitelisted by Guard"); + function getGovernanceAddress() override public view returns (address) { + return owner(); } - function whitelistAaveV3(address lendingPool, string calldata notes) external { - allowCallSite(lendingPool, getSelector("supply(address,uint256,address,uint16)"), notes); - allowCallSite(lendingPool, getSelector("withdraw(address,uint256,address)"), notes); - - allowApprovalDestination(lendingPool, notes); - } } \ No newline at end of file diff --git a/contracts/guard/src/GuardV0Base.sol b/contracts/guard/src/GuardV0Base.sol new file mode 100644 index 00000000..559b8ed9 --- /dev/null +++ b/contracts/guard/src/GuardV0Base.sol @@ -0,0 +1,502 @@ +/** + * Check for legit trade execution actions. + * + */ + +pragma solidity ^0.8.0; + +import "./lib/Path.sol"; +import "./IGuard.sol"; + +/** + * Prototype guard implementation. + * + * - Hardcoded actions for Uniswap v2, v3, 1delta, Aave + * + * - Abstract base contract to deal with different ownership modifiers and initialisers (Safe, OpenZeppelin) + * + */ +abstract contract GuardV0Base is IGuard { + using Path for bytes; + using BytesLib for bytes; + + /** + * Constants for 1delta path decoding using similar approach as Uniswap v3 `Path.sol` + * + * Check our implementation at: `validate1deltaPath()` + */ + /// @dev The length of the bytes encoded address + uint256 private constant ADDR_SIZE = 20; + /// @dev The length of the bytes encoded pool fee + uint256 private constant ONEDELTA_FEE_SIZE = 3; + /// @dev The length of the bytes encoded DEX ID + uint256 private constant ONEDELTA_PID_SIZE = 1; + /// @dev The length of the bytes encoded action + uint256 private constant ONEDELTA_ACTION_SIZE = 1; + /// @dev The offset of a single token address, fee, pid and action + uint256 private constant ONEDELTA_NEXT_OFFSET = ADDR_SIZE + ONEDELTA_FEE_SIZE + ONEDELTA_PID_SIZE + ONEDELTA_ACTION_SIZE; + /// @dev The offset of an encoded pool key + uint256 private constant ONEDELTA_POP_OFFSET = ONEDELTA_NEXT_OFFSET + ADDR_SIZE; + /// @dev The minimum length of an encoding that contains 2 or more pools + uint256 private constant ONEDELTA_MULTIPLE_POOLS_MIN_LENGTH = ONEDELTA_POP_OFFSET + ONEDELTA_NEXT_OFFSET; + + struct ExactInputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + } + + struct ExactOutputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + } + + // Allowed ERC20.approve() + mapping(address target => mapping(bytes4 selector => bool allowed)) public allowedCallSites; + + // How many call sites we have enabled all-time counter. + // + // Used for diagnostics/debugging. + // + uint public callSiteCount; + + // Allowed ERC-20 tokens we may receive or send in a trade + mapping(address token => bool allowed) public allowedAssets; + + // Allowed trade executor hot wallets + mapping(address sender => bool allowed) public allowedSenders; + + // Allowed token receivers post trade + mapping(address receiver => bool allowed) public allowedReceivers; + + // Allowed owners + mapping(address destination => bool allowed) public allowedWithdrawDestinations; + + // Allowed routers + mapping(address destination => bool allowed) public allowedApprovalDestinations; + + // Allowed routers + mapping(address destination => bool allowed) public allowedDelegationApprovalDestinations; + + event CallSiteApproved(address target, bytes4 selector, string notes); + event CallSiteRemoved(address target, bytes4 selector, string notes); + + event SenderApproved(address sender, string notes); + event SenderRemoved(address sender, string notes); + + event ReceiverApproved(address sender, string notes); + event ReceiverRemoved(address sender, string notes); + + event WithdrawDestinationApproved(address sender, string notes); + event WithdrawDestinationRemoved(address sender, string notes); + + event ApprovalDestinationApproved(address sender, string notes); + event ApprovalDestinationRemoved(address sender, string notes); + + event DelegationApprovalDestinationApproved(address sender, string notes); + event DelegationApprovalDestinationRemoved(address sender, string notes); + + event AssetApproved(address sender, string notes); + event AssetRemoved(address sender, string notes); + + modifier onlyGuardOwner() virtual; + + function getGovernanceAddress() virtual public view returns (address); + + function getSelector(string memory _func) internal pure returns (bytes4) { + // https://solidity-by-example.org/function-selector/ + return bytes4(keccak256(bytes(_func))); + } + + /** + * Track version during internal development. + * + * We bump up when new whitelistings added. + */ + function getInternalVersion() public pure returns (uint8) { + return 1; + } + + function allowCallSite(address target, bytes4 selector, string calldata notes) public onlyGuardOwner { + allowedCallSites[target][selector] = true; + callSiteCount++; + emit CallSiteApproved(target, selector, notes); + } + + function removeCallSite(address target, bytes4 selector, string calldata notes) public onlyGuardOwner { + delete allowedCallSites[target][selector]; + emit CallSiteRemoved(target, selector, notes); + } + + function allowSender(address sender, string calldata notes) public onlyGuardOwner { + allowedSenders[sender] = true; + emit SenderApproved(sender, notes); + } + + function removeSender(address sender, string calldata notes) public onlyGuardOwner { + delete allowedSenders[sender]; + emit SenderRemoved(sender, notes); + } + + function allowReceiver(address receiver, string calldata notes) public onlyGuardOwner { + allowedReceivers[receiver] = true; + emit ReceiverApproved(receiver, notes); + } + + function removeReceiver(address receiver, string calldata notes) public onlyGuardOwner { + delete allowedReceivers[receiver]; + emit ReceiverRemoved(receiver, notes); + } + + function allowWithdrawDestination(address destination, string calldata notes) public onlyGuardOwner { + allowedWithdrawDestinations[destination] = true; + emit WithdrawDestinationApproved(destination, notes); + } + + function removeWithdrawDestination(address destination, string calldata notes) public onlyGuardOwner { + delete allowedWithdrawDestinations[destination]; + emit WithdrawDestinationRemoved(destination, notes); + } + + function allowApprovalDestination(address destination, string calldata notes) public onlyGuardOwner { + allowedApprovalDestinations[destination] = true; + emit ApprovalDestinationApproved(destination, notes); + } + + function removeApprovalDestination(address destination, string calldata notes) public onlyGuardOwner { + delete allowedApprovalDestinations[destination]; + emit ApprovalDestinationRemoved(destination, notes); + } + + function allowDelegationApprovalDestination(address destination, string calldata notes) public onlyGuardOwner { + allowedDelegationApprovalDestinations[destination] = true; + emit ApprovalDestinationApproved(destination, notes); + } + + function removeDelegationApprovalDestination(address destination, string calldata notes) public onlyGuardOwner { + delete allowedApprovalDestinations[destination]; + emit ApprovalDestinationRemoved(destination, notes); + } + + function allowAsset(address asset, string calldata notes) public onlyGuardOwner { + allowedAssets[asset] = true; + emit AssetApproved(asset, notes); + } + + function removeAsset(address asset, string calldata notes) public onlyGuardOwner { + delete allowedAssets[asset]; + emit AssetRemoved(asset, notes); + } + + // Basic check if any target contract is whitelisted + function isAllowedCallSite(address target, bytes4 selector) public view returns (bool) { + return allowedCallSites[target][selector]; + } + + function isAllowedSender(address sender) public view returns (bool) { + return allowedSenders[sender] == true; + } + + // Assume any tokens are send back to the vault + function isAllowedReceiver(address receiver) public view returns (bool) { + return allowedReceivers[receiver] == true; + } + + function isAllowedWithdrawDestination(address receiver) public view returns (bool) { + return allowedWithdrawDestinations[receiver] == true; + } + + function isAllowedApprovalDestination(address receiver) public view returns (bool) { + return allowedApprovalDestinations[receiver] == true; + } + + function isAllowedDelegationApprovalDestination(address receiver) public view returns (bool) { + return allowedDelegationApprovalDestinations[receiver] == true; + } + + function isAllowedAsset(address token) public view returns (bool) { + return allowedAssets[token] == true; + } + + function validate_transfer(bytes memory callData) public view { + (address to, ) = abi.decode(callData, (address, uint)); + require(isAllowedWithdrawDestination(to), "validate_transfer: Receiver address not whitelisted by Guard"); + } + + function validate_approve(bytes memory callData) public view { + (address to, ) = abi.decode(callData, (address, uint)); + require(isAllowedApprovalDestination(to), "validate_approve: Approve address does not match"); + } + + function validate_approveDelegation(bytes memory callData) public view { + (address to, ) = abi.decode(callData, (address, uint)); + require(isAllowedDelegationApprovalDestination(to), "validate_approveDelegation: Approve delegation address does not match"); + } + + function whitelistToken(address token, string calldata notes) external { + allowCallSite(token, getSelector("transfer(address,uint256)"), notes); + allowCallSite(token, getSelector("approve(address,uint256)"), notes); + allowAsset(token, notes); + } + + function whitelistTokenForDelegation(address token, string calldata notes) external { + allowCallSite(token, getSelector("approveDelegation(address,uint256)"), notes); + allowAsset(token, notes); + } + + // Satisfy IGuard + function validateCall( + address sender, + address target, + bytes calldata callDataWithSelector + ) external view { + _validateCallInternal(sender, target, callDataWithSelector); + } + + function _validateCallInternal( + address sender, + address target, + bytes calldata callDataWithSelector + ) public view { + + if(sender == getGovernanceAddress()) { + // Governance can manually recover any issue + return; + } + + require(isAllowedSender(sender), "validateCall: Sender not allowed"); + + // Assume sender is trade-executor hot wallet + + bytes4 selector = bytes4(callDataWithSelector[:4]); + bytes calldata callData = callDataWithSelector[4:]; + require(isAllowedCallSite(target, selector), "validateCall: Call site not allowed"); + + if(selector == getSelector("swapExactTokensForTokens(uint256,uint256,address[],address,uint256)")) { + validate_swapExactTokensForTokens(callData); + } else if(selector == getSelector("exactInput((bytes,address,uint256,uint256,uint256))")) { + validate_exactInput(callData); + } else if(selector == getSelector("multicall(bytes[])")) { + validate_1deltaMulticall(callData); + } else if(selector == getSelector("transfer(address,uint256)")) { + validate_transfer(callData); + } else if(selector == getSelector("approve(address,uint256)")) { + validate_approve(callData); + } else if(selector == getSelector("approveDelegation(address,uint256)")) { + validate_approveDelegation(callData); + } else if(selector == getSelector("supply(address,uint256,address,uint16)")) { + validate_aaveSupply(callData); + } else if(selector == getSelector("withdraw(address,uint256,address)")) { + validate_aaveWithdraw(callData); + } else { + revert("Unknown function selector"); + } + } + + // Validate Uniswap v2 trade + function validate_swapExactTokensForTokens(bytes memory callData) public view { + (, , address[] memory path, address to, ) = abi.decode(callData, (uint, uint, address[], address, uint)); + + require(isAllowedReceiver(to), "validate_swapExactTokensForTokens: Receiver address not whitelisted by Guard"); + + address token; + for (uint256 i = 0; i < path.length; i++) { + token = path[i]; + require(isAllowedAsset(token), "Token not allowed"); + } + } + + function whitelistUniswapV2Router(address router, string calldata notes) external { + allowCallSite(router, getSelector("swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"), notes); + allowApprovalDestination(router, notes); + } + + // validate Uniswap v3 trade + function validate_exactInput(bytes memory callData) public view { + (ExactInputParams memory params) = abi.decode(callData, (ExactInputParams)); + + require(isAllowedReceiver(params.recipient), "validate_exactInput: Receiver address not whitelisted by Guard"); + validateUniswapV3Path(params.path); + } + + function validate_exactOutput(bytes memory callData) public view { + (ExactOutputParams memory params) = abi.decode(callData, (ExactOutputParams)); + + require(isAllowedReceiver(params.recipient), "validate_exactOutput: Receiver address not whitelisted by Guard"); + validateUniswapV3Path(params.path); + } + + function validateUniswapV3Path(bytes memory path) public view { + address tokenIn; + address tokenOut; + + while (true) { + (tokenOut, tokenIn, ) = path.decodeFirstPool(); + + require(isAllowedAsset(tokenIn), "validateUniswapV3Path: Token not allowed"); + require(isAllowedAsset(tokenOut), "validateUniswapV3Path: Token not allowed"); + + if (path.hasMultiplePools()) { + path = path.skipToken(); + } else { + break; + } + } + } + + function whitelistUniswapV3Router(address router, string calldata notes) external { + allowCallSite(router, getSelector("exactInput((bytes,address,uint256,uint256,uint256))"), notes); + allowCallSite(router, getSelector("exactOutput((bytes,address,uint256,uint256,uint256))"), notes); + allowApprovalDestination(router, notes); + } + + // validate 1delta trade + function validate_1deltaMulticall(bytes memory callData) public view { + (bytes[] memory callArr) = abi.decode(callData, (bytes[])); + + // loop through all sub-calls and validate + for (uint i; i < callArr.length; i++) { + bytes memory callDataWithSelector = callArr[i]; + + // bytes memory has to be sliced using BytesLib + bytes4 selector = bytes4(callDataWithSelector.slice(0, 4)); + bytes memory subCallData = callDataWithSelector.slice(4, callDataWithSelector.length - 4); + + // validate each sub-call + if (selector == getSelector("transferERC20In(address,uint256)")) { + validate_transferERC20In(subCallData); + } else if (selector == getSelector("transferERC20AllIn(address)")) { + validate_transferERC20AllIn(subCallData); + } else if (selector == getSelector("deposit(address,address)")) { + validate_1deltaDeposit(subCallData); + } else if (selector == getSelector("withdraw(address,address)")) { + validate_1deltaWithdraw(subCallData); + } else if (selector == getSelector("flashSwapExactIn(uint256,uint256,bytes)")) { + validate_flashSwapExactInt(subCallData); + } else if (selector == getSelector("flashSwapExactOut(uint256,uint256,bytes)")) { + validate_flashSwapExactOut(subCallData); + } else if (selector == getSelector("flashSwapAllOut(uint256,bytes)")) { + validate_flashSwapAllOut(subCallData); + } else { + revert("validate_1deltaMulticall: Unknown function selector"); + } + } + } + + // 1delta implementation: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/FlashAggregator.sol#L78-L81 + function validate_transferERC20In(bytes memory callData) public view { + (address token, ) = abi.decode(callData, (address, uint256)); + + require(isAllowedAsset(token), "validate_transferERC20In: Token not allowed"); + } + + // 1delta implementation: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/FlashAggregator.sol#L83-L93 + function validate_transferERC20AllIn(bytes memory callData) public view { + (address token) = abi.decode(callData, (address)); + + require(isAllowedAsset(token), "validate_transferERC20AllIn: Token not allowed"); + } + + // 1delta implementation: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/FlashAggregator.sol#L34-L39 + function validate_1deltaDeposit(bytes memory callData) public view { + (address token, address receiver) = abi.decode(callData, (address, address)); + + require(isAllowedAsset(token), "validate_transferERC20AllIn: Token not allowed"); + require(isAllowedReceiver(receiver), "validate_deposit: Receiver address not whitelisted by Guard"); + } + + // 1delta: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/FlashAggregator.sol#L71-L74 + function validate_1deltaWithdraw(bytes memory callData) public view { + (address token, address receiver) = abi.decode(callData, (address, address)); + + require(isAllowedAsset(token), "validate_withdraw: Token not allowed"); + require(isAllowedReceiver(receiver), "validate_deposit: Receiver address not whitelisted by Guard"); + } + + // 1delta implementation: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/MarginTrading.sol#L43-L89 + function validate_flashSwapExactInt(bytes memory callData) public view { + (, , bytes memory path) = abi.decode(callData, (uint256, uint256, bytes)); + + validate1deltaPath(path); + } + + // Reference in 1delta: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/MarginTrading.sol#L91-L103 + function validate_flashSwapExactOut(bytes memory callData) public view { + (, , bytes memory path) = abi.decode(callData, (uint256, uint256, bytes)); + + validate1deltaPath(path); + } + + // 1delta implementation: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/MarginTrading.sol#L153-L203 + function validate_flashSwapAllOut(bytes memory callData) public view { + (, bytes memory path) = abi.decode(callData, (uint256, bytes)); + + validate1deltaPath(path); + } + + /** + * Our implementation of 1delta path decoding and validation using similar + * approach as Uniswap v3 `Path.sol` + * + * Read more: + * - How 1delta encodes the path: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/test-ts/1delta/shared/aggregatorPath.ts#L5-L32 + * - How 1delta decodes the path: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/MarginTrading.sol#L54-L60 + */ + function validate1deltaPath(bytes memory path) public view { + address tokenIn; + address tokenOut; + + while (true) { + tokenIn = path.toAddress(0); + tokenOut = path.toAddress(ONEDELTA_NEXT_OFFSET); + + require(isAllowedAsset(tokenIn), "validate1deltaPath: Token not allowed"); + require(isAllowedAsset(tokenOut), "validate1deltaPath: Token not allowed"); + + // iterate to next slice if the path still contains multiple pools + if (path.length >= ONEDELTA_MULTIPLE_POOLS_MIN_LENGTH) { + path = path.slice(ONEDELTA_NEXT_OFFSET, path.length - ONEDELTA_NEXT_OFFSET); + } else { + break; + } + } + } + + function whitelistOnedelta(address brokerProxy, address lendingPool, string calldata notes) external { + allowCallSite(brokerProxy, getSelector("multicall(bytes[])"), notes); + allowApprovalDestination(brokerProxy, notes); + allowApprovalDestination(lendingPool, notes); + + // vToken has to be approved delegation for broker proxy + // Reference in 1delta tests: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/test-ts/1delta/aave/marginSwap.spec.ts#L206 + allowDelegationApprovalDestination(brokerProxy, notes); + } + + // Aave V3 implementation: https://github.com/aave/aave-v3-core/blob/e0bfed13240adeb7f05cb6cbe5e7ce78657f0621/contracts/protocol/pool/Pool.sol#L145 + function validate_aaveSupply(bytes memory callData) public view { + (address token, , , ) = abi.decode(callData, (address, uint, address, uint)); + + require(isAllowedAsset(token), "Token not allowed"); + // require(isAllowedReceiver(wallet), "Receiver address not whitelisted by Guard"); + } + + // Aave V3 implementation: https://github.com/aave/aave-v3-core/blob/e0bfed13240adeb7f05cb6cbe5e7ce78657f0621/contracts/protocol/pool/Pool.sol#L198 + function validate_aaveWithdraw(bytes memory callData) public view { + (address token, , address to) = abi.decode(callData, (address, uint, address)); + + require(isAllowedAsset(token), "Token not allowed"); + require(isAllowedReceiver(to), "Receiver address not whitelisted by Guard"); + } + + function whitelistAaveV3(address lendingPool, string calldata notes) external { + allowCallSite(lendingPool, getSelector("supply(address,uint256,address,uint16)"), notes); + allowCallSite(lendingPool, getSelector("withdraw(address,uint256,address)"), notes); + + allowApprovalDestination(lendingPool, notes); + } +} \ No newline at end of file diff --git a/contracts/guard/src/IGuard.sol b/contracts/guard/src/IGuard.sol index fbbe5f68..da656e7e 100644 --- a/contracts/guard/src/IGuard.sol +++ b/contracts/guard/src/IGuard.sol @@ -1,5 +1,14 @@ pragma solidity ^0.8.0; +/** + * Trade execution guard. + * + * - Check that we cannot do trades for which we do not have permission for + */ interface IGuard { - function validateCall(address sender, address target, bytes memory callDataWithSelector) external; + + /** + * Revert if the smart contract call is not allowed + */ + function validateCall(address sender, address target, bytes memory callDataWithSelector) external view; } \ No newline at end of file diff --git a/contracts/guard/src/MockGuard.sol b/contracts/guard/src/MockGuard.sol index f7d4c764..b5cf9801 100644 --- a/contracts/guard/src/MockGuard.sol +++ b/contracts/guard/src/MockGuard.sol @@ -15,7 +15,7 @@ contract MockGuard is IGuard { address sender, address target, bytes calldata callDataWithSelector - ) external view { + ) public view { // Don't revert } diff --git a/contracts/safe-integration/.gitignore b/contracts/safe-integration/.gitignore new file mode 100644 index 00000000..85198aaa --- /dev/null +++ b/contracts/safe-integration/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/contracts/safe-integration/README.md b/contracts/safe-integration/README.md new file mode 100644 index 00000000..2e74eafc --- /dev/null +++ b/contracts/safe-integration/README.md @@ -0,0 +1,10 @@ +Trading Strategy integration as a Safe module using Zodiac. + +This code had to be branched to its own Foundry tree due to Gnosis Safe using incompatible OpenZeppelin version. + +# Dependencies + +- Zodiac: main: https://github.com/gnosisguild/zodiac/tree/master/contractscd .. +- Safe: v1.3.0-1: https://github.com/safe-global/safe-smart-account/ +- OpenZeppelin: release-v3.4: https://github.com/OpenZeppelin/openzeppelin-contracts +- \ No newline at end of file diff --git a/contracts/safe-integration/foundry.toml b/contracts/safe-integration/foundry.toml new file mode 100644 index 00000000..3cd3634e --- /dev/null +++ b/contracts/safe-integration/foundry.toml @@ -0,0 +1,9 @@ +[profile.default] +src = "src" +out = "out" +libs = ["lib"] +allow_paths = ["../guard"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options + + diff --git a/contracts/safe-integration/lib/openzeppelin-contracts b/contracts/safe-integration/lib/openzeppelin-contracts new file mode 160000 index 00000000..68513642 --- /dev/null +++ b/contracts/safe-integration/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit 685136427d0f6fa4f4f1fe3d9975fa237ce59316 diff --git a/contracts/safe-integration/lib/openzeppelin-contracts-upgradeable b/contracts/safe-integration/lib/openzeppelin-contracts-upgradeable new file mode 160000 index 00000000..0d665b1d --- /dev/null +++ b/contracts/safe-integration/lib/openzeppelin-contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit 0d665b1d58581d153399158adc349410366061cd diff --git a/contracts/safe-integration/lib/safe-contracts b/contracts/safe-integration/lib/safe-contracts new file mode 160000 index 00000000..f3af2a9a --- /dev/null +++ b/contracts/safe-integration/lib/safe-contracts @@ -0,0 +1 @@ +Subproject commit f3af2a9a24aa47faea3dbd8ebcdf665694975a0f diff --git a/contracts/safe-integration/lib/zodiac b/contracts/safe-integration/lib/zodiac new file mode 160000 index 00000000..18b7575b --- /dev/null +++ b/contracts/safe-integration/lib/zodiac @@ -0,0 +1 @@ +Subproject commit 18b7575bb342424537883f7ebe0a94cd7f3ec4f6 diff --git a/contracts/safe-integration/remappings.txt b/contracts/safe-integration/remappings.txt new file mode 100644 index 00000000..a5614712 --- /dev/null +++ b/contracts/safe-integration/remappings.txt @@ -0,0 +1,4 @@ +@openzeppelin/contracts-upgradeable=lib/openzeppelin-contracts-upgradeable/contracts +@openzeppelin/=lib/openzeppelin-contracts/contracts +@gnosis.pm/=lib +@guard=../guard/src \ No newline at end of file diff --git a/contracts/safe-integration/src/MockSafe.sol b/contracts/safe-integration/src/MockSafe.sol new file mode 100644 index 00000000..5949586b --- /dev/null +++ b/contracts/safe-integration/src/MockSafe.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.8.0; + +/** + * Mock enableModule() + */ +contract MockSafe { + address public module; + + error NotAuthorized(address unacceptedAddress); + + receive() external payable {} + + function enableModule(address _module) external { + module = _module; + } + + function exec( + address payable to, + uint256 value, + bytes calldata data + ) external { + bool success; + bytes memory response; + (success, response) = to.call{value: value}(data); + if (!success) { + assembly { + revert(add(response, 0x20), mload(response)) + } + } + } + + function execTransactionFromModule( + address payable to, + uint256 value, + bytes calldata data, + uint8 operation + ) external returns (bool success) { + if (msg.sender != module) revert NotAuthorized(msg.sender); + if (operation == 1) (success, ) = to.delegatecall(data); + else (success, ) = to.call{value: value}(data); + } +} \ No newline at end of file diff --git a/contracts/safe-integration/src/TradingStrategyModuleV0.sol b/contracts/safe-integration/src/TradingStrategyModuleV0.sol new file mode 100644 index 00000000..18672943 --- /dev/null +++ b/contracts/safe-integration/src/TradingStrategyModuleV0.sol @@ -0,0 +1,101 @@ +/** + * Safe and Zodiac based guards. + * + * For Lagoon and Safe wallet integration. + * + * Notes on Safe Modules + * - https://gist.github.com/auryn-macmillan/841906d0bc6c2624e83598cdfac17de8 + * - https://github.com/gnosisguild/zodiac/blob/master/contracts/core/Module.sol + * - https://gist.github.com/auryn-macmillan/105ae8f09c34406997d217ee4dc0f63a + * - https://www.zodiac.wiki/documentation/custom-module + */ + +pragma solidity ^0.8.26; + +import "@gnosis.pm/zodiac/contracts/core/Module.sol"; +import "@guard/GuardV0Base.sol"; + +/** + * Trading Strategy integration as Zodiac Module. + * + * - Add automated trading strategy support w/whitelisted trading universe and + * and trade executors + * - Support Lagoon, Gnosis Safe and other Gnosis Safe-based ecosystems which support Zodiac modules + * - Owner should point to Gnosis Safe / DAO + * + * This is initial, MVP, version. + * + * Notes + * - See VelvetSafeModule as an example https://github.com/Velvet-Capital/velvet-core/blob/9d487937d0569c12e85b436a1c6f3e68a1dc8c44/contracts/vault/VelvetSafeModule.sol#L16 + * + */ +contract TradingStrategyModuleV0 is Module, GuardV0Base { + + constructor(address _owner, address _target) { + bytes memory initializeParams = abi.encode(_owner, _target); + setUp(initializeParams); + } + + // Override to use Zodiac Module's ownership mechanism + modifier onlyGuardOwner() override { + _checkOwner(); + _; + } + + /** + * Get the address of the proto DAO.@author + * + * Override to use Zodiac Module's ownership mechanism. + */ + function getGovernanceAddress() override public view returns (address) { + return owner(); + } + + /// @dev Initialize function, will be triggered when a new proxy is deployed + /// @param initializeParams Parameters of initialization encoded + /// https://gist.github.com/auryn-macmillan/841906d0bc6c2624e83598cdfac17de8 + function setUp(bytes memory initializeParams) public override initializer { + __Ownable_init(msg.sender); + (address _owner, address _target) = abi.decode(initializeParams, (address, address)); + setAvatar(_target); + setTarget(_target); + transferOwnership(_owner); + } + + /** + * The main entry point for the trade executor. + * + * - Checks for the whitelisted sender (=trade executor hot wallet) + * - Check for the allowed callsites/token whitelists/etc. + * - Execute transaction on behalf of Safe + * + */ + function performCall(address target, bytes calldata callData) external { + + bool success; + bytes memory response; + + // Check that the asset manager can perform this function. + // Will revert() on error + _validateCallInternal(msg.sender, target, callData); + + // Inherit from Module contract, + // execute a tx on behalf of Gnosis + (success, response) = execAndReturnData( + target, + 0, + callData, + Enum.Operation.Call + ); + + // Bubble up the revert reason + if (!success) { + assembly { + revert(add(response, 0x20), mload(response)) + } + } + } +} + + + diff --git a/eth_defi/abi/safe-integration/MockSafe.json b/eth_defi/abi/safe-integration/MockSafe.json new file mode 100644 index 00000000..b070639c --- /dev/null +++ b/eth_defi/abi/safe-integration/MockSafe.json @@ -0,0 +1 @@ +{"abi":[{"type":"receive","stateMutability":"payable"},{"type":"function","name":"enableModule","inputs":[{"name":"_module","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"exec","inputs":[{"name":"to","type":"address","internalType":"address payable"},{"name":"value","type":"uint256","internalType":"uint256"},{"name":"data","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"execTransactionFromModule","inputs":[{"name":"to","type":"address","internalType":"address payable"},{"name":"value","type":"uint256","internalType":"uint256"},{"name":"data","type":"bytes","internalType":"bytes"},{"name":"operation","type":"uint8","internalType":"uint8"}],"outputs":[{"name":"success","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},{"type":"function","name":"module","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"error","name":"NotAuthorized","inputs":[{"name":"unacceptedAddress","type":"address","internalType":"address"}]}],"bytecode":{"object":"0x6080604052348015600f57600080fd5b506104468061001f6000396000f3fe6080604052600436106100435760003560e01c80630565bb671461004f578063468721a714610071578063610b5925146100a6578063b86d5298146100e357600080fd5b3661004a57005b600080fd5b34801561005b57600080fd5b5061006f61006a366004610306565b61011b565b005b34801561007d57600080fd5b5061009161008c366004610362565b610197565b60405190151581526020015b60405180910390f35b3480156100b257600080fd5b5061006f6100c13660046103dc565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b3480156100ef57600080fd5b50600054610103906001600160a01b031681565b6040516001600160a01b03909116815260200161009d565b60006060856001600160a01b031685858560405161013a929190610400565b60006040518083038185875af1925050503d8060008114610177576040519150601f19603f3d011682016040523d82523d6000602084013e61017c565b606091505b5090925090508161018f57805160208201fd5b505050505050565b600080546001600160a01b031633146101c957604051634a0bfec160e01b815233600482015260240160405180910390fd5b8160ff1660010361023957856001600160a01b031684846040516101ee929190610400565b600060405180830381855af49150503d8060008114610229576040519150601f19603f3d011682016040523d82523d6000602084013e61022e565b606091505b50508091505061029c565b856001600160a01b0316858585604051610254929190610400565b60006040518083038185875af1925050503d8060008114610291576040519150601f19603f3d011682016040523d82523d6000602084013e610296565b606091505b50909150505b95945050505050565b6001600160a01b03811681146102ba57600080fd5b50565b60008083601f8401126102cf57600080fd5b50813567ffffffffffffffff8111156102e757600080fd5b6020830191508360208285010111156102ff57600080fd5b9250929050565b6000806000806060858703121561031c57600080fd5b8435610327816102a5565b935060208501359250604085013567ffffffffffffffff81111561034a57600080fd5b610356878288016102bd565b95989497509550505050565b60008060008060006080868803121561037a57600080fd5b8535610385816102a5565b945060208601359350604086013567ffffffffffffffff8111156103a857600080fd5b6103b4888289016102bd565b909450925050606086013560ff811681146103ce57600080fd5b809150509295509295909350565b6000602082840312156103ee57600080fd5b81356103f9816102a5565b9392505050565b818382376000910190815291905056fea26469706673582212200446d66091ce10af1428ad4b2f918c362021407997eb056c1cb84dfb481acccf64736f6c634300081a0033","sourceMap":"99:968:0:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x6080604052600436106100435760003560e01c80630565bb671461004f578063468721a714610071578063610b5925146100a6578063b86d5298146100e357600080fd5b3661004a57005b600080fd5b34801561005b57600080fd5b5061006f61006a366004610306565b61011b565b005b34801561007d57600080fd5b5061009161008c366004610362565b610197565b60405190151581526020015b60405180910390f35b3480156100b257600080fd5b5061006f6100c13660046103dc565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b3480156100ef57600080fd5b50600054610103906001600160a01b031681565b6040516001600160a01b03909116815260200161009d565b60006060856001600160a01b031685858560405161013a929190610400565b60006040518083038185875af1925050503d8060008114610177576040519150601f19603f3d011682016040523d82523d6000602084013e61017c565b606091505b5090925090508161018f57805160208201fd5b505050505050565b600080546001600160a01b031633146101c957604051634a0bfec160e01b815233600482015260240160405180910390fd5b8160ff1660010361023957856001600160a01b031684846040516101ee929190610400565b600060405180830381855af49150503d8060008114610229576040519150601f19603f3d011682016040523d82523d6000602084013e61022e565b606091505b50508091505061029c565b856001600160a01b0316858585604051610254929190610400565b60006040518083038185875af1925050503d8060008114610291576040519150601f19603f3d011682016040523d82523d6000602084013e610296565b606091505b50909150505b95945050505050565b6001600160a01b03811681146102ba57600080fd5b50565b60008083601f8401126102cf57600080fd5b50813567ffffffffffffffff8111156102e757600080fd5b6020830191508360208285010111156102ff57600080fd5b9250929050565b6000806000806060858703121561031c57600080fd5b8435610327816102a5565b935060208501359250604085013567ffffffffffffffff81111561034a57600080fd5b610356878288016102bd565b95989497509550505050565b60008060008060006080868803121561037a57600080fd5b8535610385816102a5565b945060208601359350604086013567ffffffffffffffff8111156103a857600080fd5b6103b4888289016102bd565b909450925050606086013560ff811681146103ce57600080fd5b809150509295509295909350565b6000602082840312156103ee57600080fd5b81356103f9816102a5565b9392505050565b818382376000910190815291905056fea26469706673582212200446d66091ce10af1428ad4b2f918c362021407997eb056c1cb84dfb481acccf64736f6c634300081a0033","sourceMap":"99:968:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;326:359;;;;;;;;;;-1:-1:-1;326:359:0;;;;;:::i;:::-;;:::i;:::-;;691:374;;;;;;;;;;-1:-1:-1;691:374:0;;;;;:::i;:::-;;:::i;:::-;;;2211:14:1;;2204:22;2186:41;;2174:2;2159:18;691:374:0;;;;;;;;239:81;;;;;;;;;;-1:-1:-1;239:81:0;;;;;:::i;:::-;297:6;:16;;-1:-1:-1;;;;;;297:16:0;-1:-1:-1;;;;;297:16:0;;;;;;;;;;239:81;123:21;;;;;;;;;;-1:-1:-1;123:21:0;;;;-1:-1:-1;;;;;123:21:0;;;;;;-1:-1:-1;;;;;2662:32:1;;;2644:51;;2632:2;2617:18;123:21:0;2498:203:1;326:359:0;445:12;467:21;520:2;-1:-1:-1;;;;;520:7:0;535:5;542:4;;520:27;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;498:49:0;;-1:-1:-1;498:49:0;-1:-1:-1;498:49:0;557:122;;646:8;640:15;633:4;623:8;619:19;612:44;557:122;435:250;;326:359;;;;:::o;691:374::-;855:12;897:6;;-1:-1:-1;;;;;897:6:0;883:10;:20;879:58;;912:25;;-1:-1:-1;;;912:25:0;;926:10;912:25;;;2644:51:1;2617:18;;912:25:0;;;;;;;879:58;951:9;:14;;964:1;951:14;947:111;;981:2;-1:-1:-1;;;;;981:15:0;997:4;;981:21;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;967:35;;;;;947:111;;;1031:2;-1:-1:-1;;;;;1031:7:0;1046:5;1053:4;;1031:27;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1017:41:0;;-1:-1:-1;;947:111:0;691:374;;;;;;;:::o;14:139:1:-;-1:-1:-1;;;;;97:31:1;;87:42;;77:70;;143:1;140;133:12;77:70;14:139;:::o;158:347::-;209:8;219:6;273:3;266:4;258:6;254:17;250:27;240:55;;291:1;288;281:12;240:55;-1:-1:-1;314:20:1;;357:18;346:30;;343:50;;;389:1;386;379:12;343:50;426:4;418:6;414:17;402:29;;478:3;471:4;462:6;454;450:19;446:30;443:39;440:59;;;495:1;492;485:12;440:59;158:347;;;;;:::o;510:680::-;606:6;614;622;630;683:2;671:9;662:7;658:23;654:32;651:52;;;699:1;696;689:12;651:52;738:9;725:23;757:39;790:5;757:39;:::i;:::-;815:5;-1:-1:-1;893:2:1;878:18;;865:32;;-1:-1:-1;974:2:1;959:18;;946:32;1001:18;990:30;;987:50;;;1033:1;1030;1023:12;987:50;1072:58;1122:7;1113:6;1102:9;1098:22;1072:58;:::i;:::-;510:680;;;;-1:-1:-1;1149:8:1;-1:-1:-1;;;;510:680:1:o;1195:846::-;1298:6;1306;1314;1322;1330;1383:3;1371:9;1362:7;1358:23;1354:33;1351:53;;;1400:1;1397;1390:12;1351:53;1439:9;1426:23;1458:39;1491:5;1458:39;:::i;:::-;1516:5;-1:-1:-1;1594:2:1;1579:18;;1566:32;;-1:-1:-1;1675:2:1;1660:18;;1647:32;1702:18;1691:30;;1688:50;;;1734:1;1731;1724:12;1688:50;1773:58;1823:7;1814:6;1803:9;1799:22;1773:58;:::i;:::-;1850:8;;-1:-1:-1;1747:84:1;-1:-1:-1;;1937:2:1;1922:18;;1909:32;1985:4;1972:18;;1960:31;;1950:59;;2005:1;2002;1995:12;1950:59;2028:7;2018:17;;;1195:846;;;;;;;;:::o;2238:255::-;2297:6;2350:2;2338:9;2329:7;2325:23;2321:32;2318:52;;;2366:1;2363;2356:12;2318:52;2405:9;2392:23;2424:39;2457:5;2424:39;:::i;:::-;2482:5;2238:255;-1:-1:-1;;;2238:255:1:o;2706:271::-;2889:6;2881;2876:3;2863:33;2845:3;2915:16;;2940:13;;;2915:16;2706:271;-1:-1:-1;2706:271:1:o","linkReferences":{}},"methodIdentifiers":{"enableModule(address)":"610b5925","exec(address,uint256,bytes)":"0565bb67","execTransactionFromModule(address,uint256,bytes,uint8)":"468721a7","module()":"b86d5298"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.26+commit.8a97fa7a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"unacceptedAddress\",\"type\":\"address\"}],\"name\":\"NotAuthorized\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_module\",\"type\":\"address\"}],\"name\":\"enableModule\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address payable\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"exec\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address payable\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"operation\",\"type\":\"uint8\"}],\"name\":\"execTransactionFromModule\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"module\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"notice\":\"Mock enableModule()\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/MockSafe.sol\":\"MockSafe\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@gnosis.pm/=lib/\",\":@guard/=../guard/src/\",\":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/\",\":@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/src/\",\":halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/\",\":safe-contracts/=lib/safe-contracts/contracts/\",\":zodiac/=lib/zodiac/contracts/\"]},\"sources\":{\"src/MockSafe.sol\":{\"keccak256\":\"0xe7ca31646cacbc8d18220086972343ec6cd1691f86fcde14d9dd0849a1978271\",\"license\":\"LGPL-3.0-only\",\"urls\":[\"bzz-raw://57d1cbf6dcabb14156d4e965c81c5a0f0c034258709144e5e9bd1ac2219c3133\",\"dweb:/ipfs/Qmdch8e6TZxMUUo7AHPrSuLJADh3GQHwyQ7XBurtdqUuzw\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.26+commit.8a97fa7a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"unacceptedAddress","type":"address"}],"type":"error","name":"NotAuthorized"},{"inputs":[{"internalType":"address","name":"_module","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"enableModule"},{"inputs":[{"internalType":"address payable","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"exec"},{"inputs":[{"internalType":"address payable","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint8","name":"operation","type":"uint8"}],"stateMutability":"nonpayable","type":"function","name":"execTransactionFromModule","outputs":[{"internalType":"bool","name":"success","type":"bool"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"module","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[],"stateMutability":"payable","type":"receive"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@gnosis.pm/=lib/","@guard/=../guard/src/","@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/","@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/","erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/","forge-std/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/","safe-contracts/=lib/safe-contracts/contracts/","zodiac/=lib/zodiac/contracts/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/MockSafe.sol":"MockSafe"},"evmVersion":"paris","libraries":{}},"sources":{"src/MockSafe.sol":{"keccak256":"0xe7ca31646cacbc8d18220086972343ec6cd1691f86fcde14d9dd0849a1978271","urls":["bzz-raw://57d1cbf6dcabb14156d4e965c81c5a0f0c034258709144e5e9bd1ac2219c3133","dweb:/ipfs/Qmdch8e6TZxMUUo7AHPrSuLJADh3GQHwyQ7XBurtdqUuzw"],"license":"LGPL-3.0-only"}},"version":1},"id":0} \ No newline at end of file diff --git a/eth_defi/abi/safe-integration/TradingStrategyModuleV0.json b/eth_defi/abi/safe-integration/TradingStrategyModuleV0.json new file mode 100644 index 00000000..95e37244 --- /dev/null +++ b/eth_defi/abi/safe-integration/TradingStrategyModuleV0.json @@ -0,0 +1 @@ +{"abi":[{"type":"constructor","inputs":[{"name":"_owner","type":"address","internalType":"address"},{"name":"_target","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"function","name":"_validateCallInternal","inputs":[{"name":"sender","type":"address","internalType":"address"},{"name":"target","type":"address","internalType":"address"},{"name":"callDataWithSelector","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"allowApprovalDestination","inputs":[{"name":"destination","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"allowAsset","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"allowCallSite","inputs":[{"name":"target","type":"address","internalType":"address"},{"name":"selector","type":"bytes4","internalType":"bytes4"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"allowDelegationApprovalDestination","inputs":[{"name":"destination","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"allowReceiver","inputs":[{"name":"receiver","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"allowSender","inputs":[{"name":"sender","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"allowWithdrawDestination","inputs":[{"name":"destination","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"allowedApprovalDestinations","inputs":[{"name":"destination","type":"address","internalType":"address"}],"outputs":[{"name":"allowed","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"allowedAssets","inputs":[{"name":"token","type":"address","internalType":"address"}],"outputs":[{"name":"allowed","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"allowedCallSites","inputs":[{"name":"target","type":"address","internalType":"address"},{"name":"selector","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"allowed","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"allowedDelegationApprovalDestinations","inputs":[{"name":"destination","type":"address","internalType":"address"}],"outputs":[{"name":"allowed","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"allowedReceivers","inputs":[{"name":"receiver","type":"address","internalType":"address"}],"outputs":[{"name":"allowed","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"allowedSenders","inputs":[{"name":"sender","type":"address","internalType":"address"}],"outputs":[{"name":"allowed","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"allowedWithdrawDestinations","inputs":[{"name":"destination","type":"address","internalType":"address"}],"outputs":[{"name":"allowed","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"avatar","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"callSiteCount","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getGovernanceAddress","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getInternalVersion","inputs":[],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"pure"},{"type":"function","name":"isAllowedApprovalDestination","inputs":[{"name":"receiver","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"isAllowedAsset","inputs":[{"name":"token","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"isAllowedCallSite","inputs":[{"name":"target","type":"address","internalType":"address"},{"name":"selector","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"isAllowedDelegationApprovalDestination","inputs":[{"name":"receiver","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"isAllowedReceiver","inputs":[{"name":"receiver","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"isAllowedSender","inputs":[{"name":"sender","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"isAllowedWithdrawDestination","inputs":[{"name":"receiver","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"owner","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"performCall","inputs":[{"name":"target","type":"address","internalType":"address"},{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"removeApprovalDestination","inputs":[{"name":"destination","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"removeAsset","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"removeCallSite","inputs":[{"name":"target","type":"address","internalType":"address"},{"name":"selector","type":"bytes4","internalType":"bytes4"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"removeDelegationApprovalDestination","inputs":[{"name":"destination","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"removeReceiver","inputs":[{"name":"receiver","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"removeSender","inputs":[{"name":"sender","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"removeWithdrawDestination","inputs":[{"name":"destination","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"renounceOwnership","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setAvatar","inputs":[{"name":"_avatar","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setTarget","inputs":[{"name":"_target","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setUp","inputs":[{"name":"initializeParams","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"target","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"transferOwnership","inputs":[{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"validate1deltaPath","inputs":[{"name":"path","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validateCall","inputs":[{"name":"sender","type":"address","internalType":"address"},{"name":"target","type":"address","internalType":"address"},{"name":"callDataWithSelector","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validateUniswapV3Path","inputs":[{"name":"path","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_1deltaDeposit","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_1deltaMulticall","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_1deltaWithdraw","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_aaveSupply","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_aaveWithdraw","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_approve","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_approveDelegation","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_exactInput","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_exactOutput","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_flashSwapAllOut","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_flashSwapExactInt","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_flashSwapExactOut","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_swapExactTokensForTokens","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_transfer","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_transferERC20AllIn","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"validate_transferERC20In","inputs":[{"name":"callData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"whitelistAaveV3","inputs":[{"name":"lendingPool","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"whitelistOnedelta","inputs":[{"name":"brokerProxy","type":"address","internalType":"address"},{"name":"lendingPool","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"whitelistToken","inputs":[{"name":"token","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"whitelistTokenForDelegation","inputs":[{"name":"token","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"whitelistUniswapV2Router","inputs":[{"name":"router","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"whitelistUniswapV3Router","inputs":[{"name":"router","type":"address","internalType":"address"},{"name":"notes","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"ApprovalDestinationApproved","inputs":[{"name":"sender","type":"address","indexed":false,"internalType":"address"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"ApprovalDestinationRemoved","inputs":[{"name":"sender","type":"address","indexed":false,"internalType":"address"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"AssetApproved","inputs":[{"name":"sender","type":"address","indexed":false,"internalType":"address"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"AssetRemoved","inputs":[{"name":"sender","type":"address","indexed":false,"internalType":"address"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"AvatarSet","inputs":[{"name":"previousAvatar","type":"address","indexed":true,"internalType":"address"},{"name":"newAvatar","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"CallSiteApproved","inputs":[{"name":"target","type":"address","indexed":false,"internalType":"address"},{"name":"selector","type":"bytes4","indexed":false,"internalType":"bytes4"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"CallSiteRemoved","inputs":[{"name":"target","type":"address","indexed":false,"internalType":"address"},{"name":"selector","type":"bytes4","indexed":false,"internalType":"bytes4"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"DelegationApprovalDestinationApproved","inputs":[{"name":"sender","type":"address","indexed":false,"internalType":"address"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"DelegationApprovalDestinationRemoved","inputs":[{"name":"sender","type":"address","indexed":false,"internalType":"address"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"Initialized","inputs":[{"name":"version","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"name":"previousOwner","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"ReceiverApproved","inputs":[{"name":"sender","type":"address","indexed":false,"internalType":"address"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"ReceiverRemoved","inputs":[{"name":"sender","type":"address","indexed":false,"internalType":"address"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"SenderApproved","inputs":[{"name":"sender","type":"address","indexed":false,"internalType":"address"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"SenderRemoved","inputs":[{"name":"sender","type":"address","indexed":false,"internalType":"address"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"TargetSet","inputs":[{"name":"previousTarget","type":"address","indexed":true,"internalType":"address"},{"name":"newTarget","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"WithdrawDestinationApproved","inputs":[{"name":"sender","type":"address","indexed":false,"internalType":"address"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"WithdrawDestinationRemoved","inputs":[{"name":"sender","type":"address","indexed":false,"internalType":"address"},{"name":"notes","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"error","name":"InvalidInitialization","inputs":[]},{"type":"error","name":"NotInitializing","inputs":[]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"name":"owner","type":"address","internalType":"address"}]},{"type":"error","name":"OwnableUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"}]}],"bytecode":{"object":"0x608060405234801561001057600080fd5b50604051613bf6380380613bf683398101604081905261002f916103fa565b604080516001600160a01b03848116602083015283168183015281518082038301815260609091019091526100638161006b565b505050610434565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff1615906001600160401b03166000811580156100b55750825b90506000826001600160401b031660011480156100d15750303b155b9050811580156100df575080155b156100fd5760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b0319166001178555831561012b57845460ff60401b1916680100000000000000001785555b610134336101bb565b6000808780602001905181019061014b91906103fa565b9092509050610159816101cf565b61016281610227565b61016b82610281565b505083156101b357845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b6101c36102c1565b6101cc81610311565b50565b6101d7610319565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f52ae88b092de36f87fb43fe794eb1381023b9c1bce563a871154022c63dce3429190a35050565b61022f610319565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f90cc2f570a6eb594b1580ea3e41247d2d73a55281889e86bd4ec2fc29c7e62d690600090a35050565b610289610319565b6001600160a01b0381166102b857604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b6101cc81610374565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff1661030f57604051631afcd79f60e31b815260040160405180910390fd5b565b6102896102c1565b3361034b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03161461030f5760405163118cdaa760e01b81523360048201526024016102af565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6001600160a01b03811681146101cc57600080fd5b6000806040838503121561040d57600080fd5b8251610418816103e5565b6020840151909250610429816103e5565b809150509250929050565b6137b3806104436000396000f3fe608060405234801561001057600080fd5b50600436106103e65760003560e01c80638c2fdf9e1161020a578063d4b8399211610125578063f20e85e0116100b8578063f76081e411610087578063f76081e414610941578063f901dc3314610954578063fa2c59c814610967578063fadbcf481461097a578063fdedfa271461099d57600080fd5b8063f20e85e014610908578063f259a073146103eb578063f26749f31461091b578063f2fde38b1461092e57600080fd5b8063eb0de042116100f4578063eb0de0421461088e578063ebe26816146108b1578063ee5462cc146108c4578063efb47bff146108d757600080fd5b8063d4b8399214610842578063d4c0fe4514610855578063d7334c9d14610868578063e98539c51461087b57600080fd5b8063a847cf4d1161019d578063c537bed01161016c578063c537bed014610809578063ca963c5014610628578063d075f9bb1461081c578063d2f73e3d1461082f57600080fd5b8063a847cf4d14610780578063a9fc3d4f146107ae578063be8c97b0146107c5578063c4cc9dba146107f657600080fd5b8063a1b6b430116101d9578063a1b6b43014610724578063a4c1cccb14610737578063a4f9edbf1461075a578063a67e1f541461076d57600080fd5b80638c2fdf9e146106e35780638da5cb5b146106f6578063957dc6c0146106fe57806398b3cc391461071157600080fd5b806359b92be9116103055780636d9a1423116102985780637325249411610267578063732524941461068f578063761bcd9c14610697578063776d1a01146106aa5780637ea44655146106bd57806386b6dbe5146106d057600080fd5b80636d9a14231461064e578063713ebf3b14610661578063715018a61461067457806372e548a91461067c57600080fd5b806363cc17f2116102d457806363cc17f2146106025780636a6d5cc8146106155780636ad11353146106285780636d5025f11461063b57600080fd5b806359b92be91461059e5780635ace1d92146105b15780635aef7de6146105c45780635e4ccace146105ef57600080fd5b80632d12d7881161037d5780633ea355511161034c5780633ea3555114610542578063425f49d0146105555780634b941268146105685780634b956bd81461058b57600080fd5b80632d12d788146104c85780632dc32a61146104db57806334ec8d7e1461050c5780633cf200251461051f57600080fd5b80631710a4f2116103b95780631710a4f21461045e5780631c123e77146104715780631d49039c146104845780632674111e146104b557600080fd5b806304a3ba25146103eb57806307ac35621461040057806307ef00cf14610413578063086cfca81461044b575b600080fd5b6103fe6103f9366004612be5565b6109ac565b005b6103fe61040e366004612ca6565b610a15565b610436610421366004612d25565b60076020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6103fe610459366004612d25565b610d56565b6103fe61046c366004612ca6565b610dae565b6103fe61047f366004612ca6565b610e64565b610436610492366004612d25565b6001600160a01b031660009081526008602052604090205460ff16151560011490565b6103fe6104c3366004612d49565b610ede565b6103fe6104d6366004612be5565b611469565b6104366104e9366004612d25565b6001600160a01b031660009081526009602052604090205460ff16151560011490565b6103fe61051a366004612be5565b6114c5565b61043661052d366004612d25565b60066020526000908152604090205460ff1681565b6103fe610550366004612be5565b61152d565b6103fe610563366004612ca6565b611558565b610436610576366004612d25565b60096020526000908152604090205460ff1681565b6103fe610599366004612ca6565b61157a565b6103fe6105ac366004612ca6565b61161f565b6103fe6105bf366004612be5565b6116a1565b6000546105d7906001600160a01b031681565b6040516001600160a01b039091168152602001610442565b6103fe6105fd366004612dca565b611700565b6103fe610610366004612be5565b611796565b6103fe610623366004612ca6565b6117f5565b6103fe610636366004612ca6565b61189d565b6103fe610649366004612d49565b6118c0565b6103fe61065c366004612be5565b6118cc565b61043661066f366004612df9565b611902565b6103fe61193a565b6103fe61068a366004612be5565b61194e565b6105d76119ad565b6103fe6106a5366004612ca6565b6119bc565b6103fe6106b8366004612d25565b611ac7565b6103fe6106cb366004612ca6565b611b21565b6103fe6106de366004612be5565b611b5e565b6103fe6106f1366004612be5565b611bbd565b6105d7611c19565b6103fe61070c366004612ca6565b611c47565b6103fe61071f366004612be5565b611d07565b6103fe610732366004612ca6565b611d66565b610436610745366004612d25565b60046020526000908152604090205460ff1681565b6103fe610768366004612ca6565b611eae565b6103fe61077b366004612be5565b611ff4565b61043661078e366004612df9565b600260209081526000928352604080842090915290825290205460ff1681565b6107b760035481565b604051908152602001610442565b6104366107d3366004612d25565b6001600160a01b031660009081526005602052604090205460ff16151560011490565b6103fe610804366004612ca6565b612050565b610436610817366004612d25565b61210b565b61043661082a366004612d25565b61212e565b6103fe61083d366004612be5565b612151565b6001546105d7906001600160a01b031681565b6103fe610863366004612ca6565b6121b5565b6103fe610876366004612dca565b6121f5565b6103fe610889366004612ca6565b612268565b61043661089c366004612d25565b60086020526000908152604090205460ff1681565b6103fe6108bf366004612ca6565b61230a565b6103fe6108d2366004612be5565b6123a1565b6104366108e5366004612d25565b6001600160a01b031660009081526007602052604090205460ff16151560011490565b6103fe610916366004612be5565b612456565b6103fe610929366004612d49565b6124ac565b6103fe61093c366004612d25565b612521565b6103fe61094f366004612ca6565b61255f565b6103fe610962366004612be5565b6125db565b6103fe610975366004612be5565b612637565b610436610988366004612d25565b60056020526000908152604090205460ff1681565b60405160018152602001610442565b6109b4612696565b6001600160a01b03831660009081526008602052604090819020805460ff19169055517fb71be9befd3ac90c1c9981d3b1161b3c2c6dcd741f13b34061aa251226a802df90610a0890859085908590612e57565b60405180910390a1505050565b600081806020019051810190610a2b9190612f19565b905060005b8151811015610d51576000828281518110610a4d57610a4d612fce565b602002602001015190506000610a7060006004846126c89092919063ffffffff16565b610a7990612fe4565b90506000610a976004808551610a8f9190613038565b8591906126c8565b6040805180820190915260208082527f7472616e736665724552433230496e28616464726573732c75696e74323536299101529050631dac092b60e11b6001600160e01b0319831601610af257610aed8161161f565b610d46565b60408051808201909152601b81527f7472616e736665724552433230416c6c496e2861646472657373290000000000602090910152635cf59cb760e11b6001600160e01b0319831601610b4857610aed81611b21565b60408051808201909152601881527f6465706f73697428616464726573732c6164647265737329000000000000000060209091015262d3ec1f60e31b6001600160e01b0319831601610b9d57610aed81612050565b60408051808201909152601981527f776974686472617728616464726573732c6164647265737329000000000000006020909101526306bf1c7b60e01b6001600160e01b0319831601610bf357610aed8161255f565b610c1a60405180606001604052806027815260200161372360279139805160209091012090565b6001600160e01b031916826001600160e01b03191603610c3d57610aed8161189d565b610c646040518060600160405280602881526020016136fb60289139805160209091012090565b6001600160e01b031916826001600160e01b03191603610c8757610aed8161189d565b60408051808201909152601e81527f666c61736853776170416c6c4f75742875696e743235362c627974657329000060209091015263d2b3d16560e01b6001600160e01b0319831601610cdd57610aed81611558565b60405162461bcd60e51b815260206004820152603360248201527f76616c69646174655f3164656c74614d756c746963616c6c3a20556e6b6e6f776044820152723710333ab731ba34b7b71039b2b632b1ba37b960691b60648201526084015b60405180910390fd5b505050600101610a30565b505050565b610d5e612696565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f52ae88b092de36f87fb43fe794eb1381023b9c1bce563a871154022c63dce3429190a35050565b600081806020019051810190610dc49190613056565b509050610dee816001600160a01b031660009081526007602052604090205460ff16151560011490565b610e605760405162461bcd60e51b815260206004820152603c60248201527f76616c69646174655f7472616e736665723a205265636569766572206164647260448201527f657373206e6f742077686974656c6973746564206279204775617264000000006064820152608401610d3d565b5050565b6000805b610e718361281f565b5092509050610e7f8261210b565b610e9b5760405162461bcd60e51b8152600401610d3d90613084565b610ea48161210b565b610ec05760405162461bcd60e51b8152600401610d3d90613084565b610ec98361285b565b15610d5157610ed783612895565b9250610e68565b610ee66119ad565b6001600160a01b0316846001600160a01b0316031561146357610f26846001600160a01b031660009081526005602052604090205460ff16151560011490565b610f725760405162461bcd60e51b815260206004820181905260248201527f76616c696461746543616c6c3a2053656e646572206e6f7420616c6c6f7765646044820152606401610d3d565b6000610f8160048284866130cc565b610f8a916130f6565b9050366000610f9c84600481886130cc565b91509150610faa8684611902565b6110025760405162461bcd60e51b815260206004820152602360248201527f76616c696461746543616c6c3a2043616c6c2073697465206e6f7420616c6c6f6044820152621dd95960ea1b6064820152608401610d3d565b61102960405180608001604052806043815260200161361c60439139805160209091012090565b6001600160e01b031916836001600160e01b031916036110875761108282828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506119bc92505050565b61145f565b6110ae6040518060600160405280603381526020016136c860339139805160209091012090565b6001600160e01b031916836001600160e01b031916036111075761108282828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061230a92505050565b6040805180820190915260128152716d756c746963616c6c2862797465735b5d2960701b602090910152630a6d35e560e31b6001600160e01b03198416016111885761108282828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610a1592505050565b6040805180820190915260198152787472616e7366657228616464726573732c75696e743235362960381b6020909101526356fa634560e01b6001600160e01b03198416016112105761108282828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610dae92505050565b604080518082019091526018815277617070726f766528616464726573732c75696e743235362960401b60209091015263f6a1584d60e01b6001600160e01b03198416016112975761108282828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061157a92505050565b6112be6040518060600160405280602281526020016136a660229139805160209091012090565b6001600160e01b031916836001600160e01b031916036113175761108282828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611c4792505050565b61133e60405180606001604052806026815260200161365f60269139805160209091012090565b6001600160e01b031916836001600160e01b031916036113975761108282828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506121b592505050565b6113be60405180606001604052806021815260200161368560219139805160209091012090565b6001600160e01b031916836001600160e01b031916036114175761108282828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506117f592505050565b60405162461bcd60e51b815260206004820152601960248201527f556e6b6e6f776e2066756e6374696f6e2073656c6563746f72000000000000006044820152606401610d3d565b5050505b50505050565b611471612696565b6001600160a01b03831660009081526006602052604090819020805460ff19169055517f4e13b11ab98e672bd78295ef9cebe764dc617f95decf47d842c25b83abc0c72490610a0890859085908590612e57565b6114f7836114f06040518060600160405280603381526020016136c860339139805160209091012090565b8484611700565b611522836114f060405180606001604052806034815260200161374a60349139805160209091012090565b610d51838383611d07565b611522836114f060405180608001604052806043815260200161361c60439139805160209091012090565b60008180602001905181019061156e919061312e565b915050610e6081611d66565b6000818060200190518101906115909190613056565b5090506115ba816001600160a01b031660009081526008602052604090205460ff16151560011490565b610e605760405162461bcd60e51b815260206004820152603060248201527f76616c69646174655f617070726f76653a20417070726f76652061646472657360448201526f0e640c8decae640dcdee840dac2e8c6d60831b6064820152608401610d3d565b6000818060200190518101906116359190613056565b5090506116418161210b565b610e605760405162461bcd60e51b815260206004820152602b60248201527f76616c69646174655f7472616e736665724552433230496e3a20546f6b656e2060448201526a1b9bdd08185b1b1bddd95960aa1b6064820152608401610d3d565b6116a9612696565b6001600160a01b03831660009081526007602052604090819020805460ff19166001179055517f3562181221a42a19ddd03a82dfe06acab1905ceb65cdaf7d86a1d9fec664355290610a0890859085908590612e57565b611708612696565b6001600160a01b03841660009081526002602090815260408083206001600160e01b0319871684529091528120805460ff19166001179055600380549161174e83613175565b91905055507fef729aaa41b9fd994f9ff7c1960df214a84f722002e6cfbea31799cd0873a3ef84848484604051611788949392919061318e565b60405180910390a150505050565b61179e612696565b6001600160a01b03831660009081526009602052604090819020805460ff19166001179055517f628a44970c0e450415e3ae74334ea44f3307b74dbf677a1371190242bf2f358990610a0890859085908590612e57565b6000808280602001905181019061180c91906131cd565b925050915061181a8261210b565b6118365760405162461bcd60e51b8152600401610d3d90613210565b61183f8161212e565b610d515760405162461bcd60e51b815260206004820152602960248201527f52656365697665722061646472657373206e6f742077686974656c697374656460448201526808189e4811dd585c9960ba1b6064820152608401610d3d565b6000818060200190518101906118b3919061323b565b92505050610e6081611d66565b61146384848484610ede565b6118f7836114f06040518060600160405280602281526020016136a660229139805160209091012090565b610d51838383611b5e565b6001600160a01b03821660009081526002602090815260408083206001600160e01b03198516845290915290205460ff165b92915050565b611942612696565b61194c60006128c6565b565b611956612696565b6001600160a01b03831660009081526006602052604090819020805460ff19166001179055517f62dd88c5ecfa60713a657640ebec4de26fc1aefa4afdb24e6d15a124fce7277990610a0890859085908590612e57565b60006119b7611c19565b905090565b600080828060200190518101906119d3919061328b565b509350935050506119e38161212e565b611a6a5760405162461bcd60e51b815260206004820152604c60248201527f76616c69646174655f737761704578616374546f6b656e73466f72546f6b656e60448201527f733a2052656365697665722061646472657373206e6f742077686974656c697360648201526b1d195908189e4811dd585c9960a21b608482015260a401610d3d565b6000805b8351811015611ac057838181518110611a8957611a89612fce565b60200260200101519150611a9c8261210b565b611ab85760405162461bcd60e51b8152600401610d3d90613210565b600101611a6e565b5050505050565b611acf612696565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f90cc2f570a6eb594b1580ea3e41247d2d73a55281889e86bd4ec2fc29c7e62d690600090a35050565b600081806020019051810190611b37919061335b565b9050611b428161210b565b610e605760405162461bcd60e51b8152600401610d3d90613378565b611b66612696565b6001600160a01b03831660009081526004602052604090819020805460ff19166001179055517fad90e2570fc4fe9f7437be3188d5c791c6662892d31731992aa88f5c8762198390610a0890859085908590612e57565b611bc5612696565b6001600160a01b03831660009081526007602052604090819020805460ff19169055517f1212f8ddb39de8e1a339460c95752d61100723efeaa34ccd94bca94393f4093890610a0890859085908590612e57565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b600081806020019051810190611c5d9190613056565b509050611c87816001600160a01b031660009081526009602052604090205460ff16151560011490565b610e605760405162461bcd60e51b815260206004820152604560248201527f76616c69646174655f617070726f766544656c65676174696f6e3a204170707260448201527f6f76652064656c65676174696f6e206164647265737320646f6573206e6f74206064820152640dac2e8c6d60db1b608482015260a401610d3d565b611d0f612696565b6001600160a01b03831660009081526008602052604090819020805460ff19166001179055517f628a44970c0e450415e3ae74334ea44f3307b74dbf677a1371190242bf2f358990610a0890859085908590612e57565b6000805b611d75836000612937565b9150611da4600180611d89600360146133c6565b611d9391906133c6565b611d9d91906133c6565b8490612937565b9050611daf8261210b565b611dcb5760405162461bcd60e51b8152600401610d3d906133d9565b611dd48161210b565b611df05760405162461bcd60e51b8152600401610d3d906133d9565b600180611dff600360146133c6565b611e0991906133c6565b611e1391906133c6565b6014600180611e236003846133c6565b611e2d91906133c6565b611e3791906133c6565b611e4191906133c6565b611e4b91906133c6565b835110610d5157611ea7600180611e64600360146133c6565b611e6e91906133c6565b611e7891906133c6565b600180611e87600360146133c6565b611e9191906133c6565b611e9b91906133c6565b8551610a8f9190613038565b9250611d6a565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b0316600081158015611ef35750825b90506000826001600160401b03166001148015611f0f5750303b155b905081158015611f1d575080155b15611f3b5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611f6557845460ff60401b1916600160401b1785555b611f6e336129eb565b60008087806020019051810190611f85919061341e565b91509150611f9281610d56565b611f9b81611ac7565b611fa482612521565b50508315611fec57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b611ffc612696565b6001600160a01b03831660009081526005602052604090819020805460ff19169055517f3097928509c53a2dab9500431201d82b0d756e8f890fd01f4ae6b33b45687ea490610a0890859085908590612e57565b60008082806020019051810190612067919061341e565b915091506120748261210b565b6120905760405162461bcd60e51b8152600401610d3d90613378565b6120998161212e565b610d515760405162461bcd60e51b815260206004820152603b60248201527f76616c69646174655f6465706f7369743a20526563656976657220616464726560448201527f7373206e6f742077686974656c697374656420627920477561726400000000006064820152608401610d3d565b6001600160a01b031660009081526004602052604090205460ff16151560011490565b6001600160a01b031660009081526006602052604090205460ff16151560011490565b6000606061216133868686610ede565b6121a385600086868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092018290525092506129fc915050565b909250905081611ac057805160208201fd5b6000818060200190518101906121cb9190613458565b50505090506121d98161210b565b610e605760405162461bcd60e51b8152600401610d3d90613210565b6121fd612696565b6001600160a01b03841660009081526002602090815260408083206001600160e01b03198716845290915290819020805460ff19169055517f37ea10f2d08f5a9803dfcd5abf3cfc7b0d6fcdf5fcbc36be9ea5a0b53d9a4d9b9061178890869086908690869061318e565b60008180602001905181019061227e919061353a565b905061228d816020015161212e565b6122ff5760405162461bcd60e51b815260206004820152603f60248201527f76616c69646174655f65786163744f75747075743a205265636569766572206160448201527f646472657373206e6f742077686974656c6973746564206279204775617264006064820152608401610d3d565b8051610e6090610e64565b600081806020019051810190612320919061353a565b905061232f816020015161212e565b6122ff5760405162461bcd60e51b815260206004820152603e60248201527f76616c69646174655f6578616374496e7075743a20526563656976657220616460448201527f6472657373206e6f742077686974656c697374656420627920477561726400006064820152608401610d3d565b6040805180820190915260198152787472616e7366657228616464726573732c75696e743235362960381b6020909101526123fc837fa9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b6114f0565b604080518082019091526018815277617070726f766528616464726573732c75696e743235362960401b6020909101526118f7837f095ea7b334ae44009aa867bfb386f5c3b4b443ac6f0ee573fa91c4608fbadfba6114f0565b612481836114f060405180606001604052806026815260200161365f60269139805160209091012090565b611522836114f060405180606001604052806021815260200161368560219139805160209091012090565b6040805180820190915260128152716d756c746963616c6c2862797465735b5d2960701b602090910152612500847fac9650d882acfa253cba1ed543b9ff47351da99c3bd00a89625e5cdb2099009c6114f0565b61250b848383611d07565b612516838383611d07565b611463848383611796565b612529612696565b6001600160a01b03811661255357604051631e4fbdf760e01b815260006004820152602401610d3d565b61255c816128c6565b50565b60008082806020019051810190612576919061341e565b915091506125838261210b565b6120905760405162461bcd60e51b8152602060048201526024808201527f76616c69646174655f77697468647261773a20546f6b656e206e6f7420616c6c6044820152631bddd95960e21b6064820152608401610d3d565b6125e3612696565b6001600160a01b03831660009081526004602052604090819020805460ff19169055517f9ca3f065622f5f03f32b7157677a0e420c3a36ab45fd49f256ffebce3e31058790610a0890859085908590612e57565b61263f612696565b6001600160a01b03831660009081526005602052604090819020805460ff19166001179055517fa8f9caaf4861720900294428e4ff34d37070c37afd26d96e6e4da75326d2c3ad90610a0890859085908590612e57565b3361269f611c19565b6001600160a01b03161461194c5760405163118cdaa760e01b8152336004820152602401610d3d565b6060816126d681601f6133c6565b10156127155760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606401610d3d565b8261272083826133c6565b101561275f5760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606401610d3d565b61276982846133c6565b845110156127ad5760405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b6044820152606401610d3d565b6060821580156127cc5760405191506000825260208201604052612816565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156128055780518352602092830192016127ed565b5050858452601f01601f1916604052505b50949350505050565b6000808061282d8482612937565b925061283a846014612a8c565b905061285261284b600360146133c6565b8590612937565b91509193909250565b6000612869600360146133c6565b60146128766003826133c6565b61288091906133c6565b61288a91906133c6565b825110159050919050565b60606119346128a6600360146133c6565b6128b2600360146133c6565b84516128be9190613038565b8491906126c8565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6000816129458160146133c6565b10156129885760405162461bcd60e51b8152602060048201526012602482015271746f416464726573735f6f766572666c6f7760701b6044820152606401610d3d565b6129938260146133c6565b835110156129db5760405162461bcd60e51b8152602060048201526015602482015274746f416464726573735f6f75744f66426f756e647360581b6044820152606401610d3d565b500160200151600160601b900490565b6129f3612b37565b61255c81612b80565b600154604051635229073f60e01b81526000916060916001600160a01b0390911690635229073f90612a3890899089908990899060040161356e565b6000604051808303816000875af1158015612a57573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612a7f91908101906135dc565b9150915094509492505050565b600081612a9a8160036133c6565b1015612adc5760405162461bcd60e51b8152602060048201526011602482015270746f55696e7432345f6f766572666c6f7760781b6044820152606401610d3d565b612ae78260036133c6565b83511015612b2e5760405162461bcd60e51b8152602060048201526014602482015273746f55696e7432345f6f75744f66426f756e647360601b6044820152606401610d3d565b50016003015190565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661194c57604051631afcd79f60e31b815260040160405180910390fd5b612529612b37565b6001600160a01b038116811461255c57600080fd5b60008083601f840112612baf57600080fd5b5081356001600160401b03811115612bc657600080fd5b602083019150836020828501011115612bde57600080fd5b9250929050565b600080600060408486031215612bfa57600080fd5b8335612c0581612b88565b925060208401356001600160401b03811115612c2057600080fd5b612c2c86828701612b9d565b9497909650939450505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715612c7757612c77612c39565b604052919050565b60006001600160401b03821115612c9857612c98612c39565b50601f01601f191660200190565b600060208284031215612cb857600080fd5b81356001600160401b03811115612cce57600080fd5b8201601f81018413612cdf57600080fd5b8035612cf2612ced82612c7f565b612c4f565b818152856020838501011115612d0757600080fd5b81602084016020830137600091810160200191909152949350505050565b600060208284031215612d3757600080fd5b8135612d4281612b88565b9392505050565b60008060008060608587031215612d5f57600080fd5b8435612d6a81612b88565b93506020850135612d7a81612b88565b925060408501356001600160401b03811115612d9557600080fd5b612da187828801612b9d565b95989497509550505050565b80356001600160e01b031981168114612dc557600080fd5b919050565b60008060008060608587031215612de057600080fd5b8435612deb81612b88565b9350612d7a60208601612dad565b60008060408385031215612e0c57600080fd5b8235612e1781612b88565b9150612e2560208401612dad565b90509250929050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b0384168152604060208201819052600090612e7c9083018486612e2e565b95945050505050565b60006001600160401b03821115612e9e57612e9e612c39565b5060051b60200190565b60005b83811015612ec3578181015183820152602001612eab565b50506000910152565b600082601f830112612edd57600080fd5b8151612eeb612ced82612c7f565b818152846020838601011115612f0057600080fd5b612f11826020830160208701612ea8565b949350505050565b600060208284031215612f2b57600080fd5b81516001600160401b03811115612f4157600080fd5b8201601f81018413612f5257600080fd5b8051612f60612ced82612e85565b8082825260208201915060208360051b850101925086831115612f8257600080fd5b602084015b83811015612fc35780516001600160401b03811115612fa557600080fd5b612fb489602083890101612ecc565b84525060209283019201612f87565b509695505050505050565b634e487b7160e01b600052603260045260246000fd5b805160208201516001600160e01b031981169190600482101561301b576001600160e01b0319600483900360031b81901b82161692505b5050919050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561193457611934613022565b8051612dc581612b88565b6000806040838503121561306957600080fd5b825161307481612b88565b6020939093015192949293505050565b60208082526028908201527f76616c6964617465556e69737761705633506174683a20546f6b656e206e6f7460408201526708185b1b1bddd95960c21b606082015260800190565b600080858511156130dc57600080fd5b838611156130e957600080fd5b5050820193919092039150565b80356001600160e01b03198116906004841015613127576001600160e01b0319600485900360031b81901b82161691505b5092915050565b6000806040838503121561314157600080fd5b825160208401519092506001600160401b0381111561315f57600080fd5b61316b85828601612ecc565b9150509250929050565b60006001820161318757613187613022565b5060010190565b6001600160a01b03851681526001600160e01b0319841660208201526060604082018190526000906131c39083018486612e2e565b9695505050505050565b6000806000606084860312156131e257600080fd5b83516131ed81612b88565b60208501516040860151919450925061320581612b88565b809150509250925092565b602080825260119082015270151bdad95b881b9bdd08185b1b1bddd959607a1b604082015260600190565b60008060006060848603121561325057600080fd5b83516020850151604086015191945092506001600160401b0381111561327557600080fd5b61328186828701612ecc565b9150509250925092565b600080600080600060a086880312156132a357600080fd5b85516020870151604088015191965094506001600160401b038111156132c857600080fd5b8601601f810188136132d957600080fd5b80516132e7612ced82612e85565b8082825260208201915060208360051b85010192508a83111561330957600080fd5b6020840193505b8284101561333457835161332381612b88565b825260209384019390910190613310565b9550613346925050506060870161304b565b60809690960151949793965091949392915050565b60006020828403121561336d57600080fd5b8151612d4281612b88565b6020808252602e908201527f76616c69646174655f7472616e736665724552433230416c6c496e3a20546f6b60408201526d195b881b9bdd08185b1b1bddd95960921b606082015260800190565b8082018082111561193457611934613022565b60208082526025908201527f76616c69646174653164656c7461506174683a20546f6b656e206e6f7420616c6040820152641b1bddd95960da1b606082015260800190565b6000806040838503121561343157600080fd5b825161343c81612b88565b602084015190925061344d81612b88565b809150509250929050565b6000806000806080858703121561346e57600080fd5b845161347981612b88565b60208601516040870151919550935061349181612b88565b6060959095015193969295505050565b600060a082840312156134b357600080fd5b60405160a081016001600160401b03811182821017156134d5576134d5612c39565b806040525080915082516001600160401b038111156134f357600080fd5b6134ff85828601612ecc565b825250602083015161351081612b88565b60208201526040838101519082015260608084015190820152608092830151920191909152919050565b60006020828403121561354c57600080fd5b81516001600160401b0381111561356257600080fd5b612f11848285016134a1565b60018060a01b038516815283602082015260806040820152600083518060808401526135a18160a0850160208801612ea8565b601f01601f1916820160a0019050600283106135cd57634e487b7160e01b600052602160045260246000fd5b82606083015295945050505050565b600080604083850312156135ef57600080fd5b825180151581146135ff57600080fd5b60208401519092506001600160401b0381111561315f57600080fdfe737761704578616374546f6b656e73466f72546f6b656e732875696e743235362c75696e743235362c616464726573735b5d2c616464726573732c75696e7432353629737570706c7928616464726573732c75696e743235362c616464726573732c75696e74313629776974686472617728616464726573732c75696e743235362c6164647265737329617070726f766544656c65676174696f6e28616464726573732c75696e74323536296578616374496e707574282862797465732c616464726573732c75696e743235362c75696e743235362c75696e743235362929666c6173685377617045786163744f75742875696e743235362c75696e743235362c627974657329666c617368537761704578616374496e2875696e743235362c75696e743235362c62797465732965786163744f7574707574282862797465732c616464726573732c75696e743235362c75696e743235362c75696e743235362929a2646970667358221220e57df6ec2f8bed9582c2ebb689735b1f77e8164ce6e68394e4e0543b95769a0064736f6c634300081a0033","sourceMap":"1060:1936:14:-:0;;;1123:154;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1210:27;;;-1:-1:-1;;;;;732:32:15;;;1210:27:14;;;714:51:15;801:32;;781:18;;;774:60;1210:27:14;;;;;;;;;687:18:15;;;;1210:27:14;;;1247:23;1210:27;1247:5;:23::i;:::-;1168:109;1123:154;;1060:1936;;1760:302;8870:21:8;4302:15;;;;;;;4301:16;;-1:-1:-1;;;;;4348:14:8;4158:30;4726:16;;:34;;;;;4746:14;4726:34;4706:54;;4770:17;4790:11;-1:-1:-1;;;;;4790:16:8;4805:1;4790:16;:50;;;;-1:-1:-1;4818:4:8;4810:25;:30;4790:50;4770:70;;4856:12;4855:13;:30;;;;;4873:12;4872:13;4855:30;4851:91;;;4908:23;;-1:-1:-1;;;4908:23:8;;;;;;;;;;;4851:91;4951:18;;-1:-1:-1;;;;;;4951:18:8;4968:1;4951:18;;;4979:67;;;;5013:22;;-1:-1:-1;;;;5013:22:8;;;;;4979:67;1844:26:14::1;1859:10;1844:14;:26::i;:::-;1881:14;1897:15:::0;1927:16:::1;1916:48;;;;;;;;;;;;:::i;:::-;1880:84:::0;;-1:-1:-1;1880:84:14;-1:-1:-1;1974:18:14::1;1880:84:::0;1974:9:::1;:18::i;:::-;2002;2012:7:::0;2002:9:::1;:18::i;:::-;2030:25;2048:6:::0;2030:17:::1;:25::i;:::-;1834:228;;5070:14:8::0;5066:101;;;5100:23;;-1:-1:-1;;;;5100:23:8;;;5142:14;;-1:-1:-1;1404:50:15;;5142:14:8;;1392:2:15;1377:18;5142:14:8;;;;;;;5066:101;4092:1081;;;;;1760:302:14;:::o;1847:127:7:-;6931:20:8;:18;:20::i;:::-;1929:38:7::1;1954:12:::0;1929:24:::1;:38::i;:::-;1847:127:::0;:::o;980:162:11:-;2334:13:7;:11;:13::i;:::-;1039:22:11::1;1064:6:::0;;-1:-1:-1;;;;;1076:16:11;;::::1;-1:-1:-1::0;;;;;;1076:16:11;::::1;::::0;::::1;::::0;;1103:34:::1;::::0;1064:6;;;::::1;::::0;;;1103:34:::1;::::0;1039:22;1103:34:::1;1033:109;980:162:::0;:::o;1259:::-;2334:13:7;:11;:13::i;:::-;1343:6:11::1;::::0;;-1:-1:-1;;;;;1355:16:11;;::::1;-1:-1:-1::0;;;;;;1355:16:11;::::1;::::0;::::1;::::0;;;1382:34:::1;::::0;1343:6;::::1;::::0;1355:16;1343:6;;1382:34:::1;::::0;1318:22:::1;::::0;1382:34:::1;1312:109;1259:162:::0;:::o;3405:215:7:-;2334:13;:11;:13::i;:::-;-1:-1:-1;;;;;3489:22:7;::::1;3485:91;;3534:31;::::0;-1:-1:-1;;;3534:31:7;;3562:1:::1;3534:31;::::0;::::1;1611:51:15::0;1584:18;;3534:31:7::1;;;;;;;;3485:91;3585:28;3604:8:::0;3585:18:::1;:28::i;7084:141:8:-:0;8870:21;8560:40;;;;;;7146:73;;7191:17;;-1:-1:-1;;;7191:17:8;;;;;;;;;;;7146:73;7084:141::o;1980:235:7:-;6931:20:8;:18;:20::i;2658:162:7:-;966:10:9;2717:7:7;1313:22;2570:8;-1:-1:-1;;;;;2570:8:7;;2441:144;2717:7;-1:-1:-1;;;;;2717:23:7;;2713:101;;2763:40;;-1:-1:-1;;;2763:40:7;;966:10:9;2763:40:7;;;1611:51:15;1584:18;;2763:40:7;1465:203:15;3774:248:7;1313:22;3923:8;;-1:-1:-1;;;;;;3941:19:7;;-1:-1:-1;;;;;3941:19:7;;;;;;;;3975:40;;3923:8;;;;;3975:40;;3847:24;;3975:40;3837:185;;3774:248;:::o;14:131:15:-;-1:-1:-1;;;;;89:31:15;;79:42;;69:70;;135:1;132;125:12;150:385;229:6;237;290:2;278:9;269:7;265:23;261:32;258:52;;;306:1;303;296:12;258:52;338:9;332:16;357:31;382:5;357:31;:::i;:::-;457:2;442:18;;436:25;407:5;;-1:-1:-1;470:33:15;436:25;470:33;:::i;:::-;522:7;512:17;;;150:385;;;;;:::o;1465:203::-;1060:1936:14;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561001057600080fd5b50600436106103e65760003560e01c80638c2fdf9e1161020a578063d4b8399211610125578063f20e85e0116100b8578063f76081e411610087578063f76081e414610941578063f901dc3314610954578063fa2c59c814610967578063fadbcf481461097a578063fdedfa271461099d57600080fd5b8063f20e85e014610908578063f259a073146103eb578063f26749f31461091b578063f2fde38b1461092e57600080fd5b8063eb0de042116100f4578063eb0de0421461088e578063ebe26816146108b1578063ee5462cc146108c4578063efb47bff146108d757600080fd5b8063d4b8399214610842578063d4c0fe4514610855578063d7334c9d14610868578063e98539c51461087b57600080fd5b8063a847cf4d1161019d578063c537bed01161016c578063c537bed014610809578063ca963c5014610628578063d075f9bb1461081c578063d2f73e3d1461082f57600080fd5b8063a847cf4d14610780578063a9fc3d4f146107ae578063be8c97b0146107c5578063c4cc9dba146107f657600080fd5b8063a1b6b430116101d9578063a1b6b43014610724578063a4c1cccb14610737578063a4f9edbf1461075a578063a67e1f541461076d57600080fd5b80638c2fdf9e146106e35780638da5cb5b146106f6578063957dc6c0146106fe57806398b3cc391461071157600080fd5b806359b92be9116103055780636d9a1423116102985780637325249411610267578063732524941461068f578063761bcd9c14610697578063776d1a01146106aa5780637ea44655146106bd57806386b6dbe5146106d057600080fd5b80636d9a14231461064e578063713ebf3b14610661578063715018a61461067457806372e548a91461067c57600080fd5b806363cc17f2116102d457806363cc17f2146106025780636a6d5cc8146106155780636ad11353146106285780636d5025f11461063b57600080fd5b806359b92be91461059e5780635ace1d92146105b15780635aef7de6146105c45780635e4ccace146105ef57600080fd5b80632d12d7881161037d5780633ea355511161034c5780633ea3555114610542578063425f49d0146105555780634b941268146105685780634b956bd81461058b57600080fd5b80632d12d788146104c85780632dc32a61146104db57806334ec8d7e1461050c5780633cf200251461051f57600080fd5b80631710a4f2116103b95780631710a4f21461045e5780631c123e77146104715780631d49039c146104845780632674111e146104b557600080fd5b806304a3ba25146103eb57806307ac35621461040057806307ef00cf14610413578063086cfca81461044b575b600080fd5b6103fe6103f9366004612be5565b6109ac565b005b6103fe61040e366004612ca6565b610a15565b610436610421366004612d25565b60076020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6103fe610459366004612d25565b610d56565b6103fe61046c366004612ca6565b610dae565b6103fe61047f366004612ca6565b610e64565b610436610492366004612d25565b6001600160a01b031660009081526008602052604090205460ff16151560011490565b6103fe6104c3366004612d49565b610ede565b6103fe6104d6366004612be5565b611469565b6104366104e9366004612d25565b6001600160a01b031660009081526009602052604090205460ff16151560011490565b6103fe61051a366004612be5565b6114c5565b61043661052d366004612d25565b60066020526000908152604090205460ff1681565b6103fe610550366004612be5565b61152d565b6103fe610563366004612ca6565b611558565b610436610576366004612d25565b60096020526000908152604090205460ff1681565b6103fe610599366004612ca6565b61157a565b6103fe6105ac366004612ca6565b61161f565b6103fe6105bf366004612be5565b6116a1565b6000546105d7906001600160a01b031681565b6040516001600160a01b039091168152602001610442565b6103fe6105fd366004612dca565b611700565b6103fe610610366004612be5565b611796565b6103fe610623366004612ca6565b6117f5565b6103fe610636366004612ca6565b61189d565b6103fe610649366004612d49565b6118c0565b6103fe61065c366004612be5565b6118cc565b61043661066f366004612df9565b611902565b6103fe61193a565b6103fe61068a366004612be5565b61194e565b6105d76119ad565b6103fe6106a5366004612ca6565b6119bc565b6103fe6106b8366004612d25565b611ac7565b6103fe6106cb366004612ca6565b611b21565b6103fe6106de366004612be5565b611b5e565b6103fe6106f1366004612be5565b611bbd565b6105d7611c19565b6103fe61070c366004612ca6565b611c47565b6103fe61071f366004612be5565b611d07565b6103fe610732366004612ca6565b611d66565b610436610745366004612d25565b60046020526000908152604090205460ff1681565b6103fe610768366004612ca6565b611eae565b6103fe61077b366004612be5565b611ff4565b61043661078e366004612df9565b600260209081526000928352604080842090915290825290205460ff1681565b6107b760035481565b604051908152602001610442565b6104366107d3366004612d25565b6001600160a01b031660009081526005602052604090205460ff16151560011490565b6103fe610804366004612ca6565b612050565b610436610817366004612d25565b61210b565b61043661082a366004612d25565b61212e565b6103fe61083d366004612be5565b612151565b6001546105d7906001600160a01b031681565b6103fe610863366004612ca6565b6121b5565b6103fe610876366004612dca565b6121f5565b6103fe610889366004612ca6565b612268565b61043661089c366004612d25565b60086020526000908152604090205460ff1681565b6103fe6108bf366004612ca6565b61230a565b6103fe6108d2366004612be5565b6123a1565b6104366108e5366004612d25565b6001600160a01b031660009081526007602052604090205460ff16151560011490565b6103fe610916366004612be5565b612456565b6103fe610929366004612d49565b6124ac565b6103fe61093c366004612d25565b612521565b6103fe61094f366004612ca6565b61255f565b6103fe610962366004612be5565b6125db565b6103fe610975366004612be5565b612637565b610436610988366004612d25565b60056020526000908152604090205460ff1681565b60405160018152602001610442565b6109b4612696565b6001600160a01b03831660009081526008602052604090819020805460ff19169055517fb71be9befd3ac90c1c9981d3b1161b3c2c6dcd741f13b34061aa251226a802df90610a0890859085908590612e57565b60405180910390a1505050565b600081806020019051810190610a2b9190612f19565b905060005b8151811015610d51576000828281518110610a4d57610a4d612fce565b602002602001015190506000610a7060006004846126c89092919063ffffffff16565b610a7990612fe4565b90506000610a976004808551610a8f9190613038565b8591906126c8565b6040805180820190915260208082527f7472616e736665724552433230496e28616464726573732c75696e74323536299101529050631dac092b60e11b6001600160e01b0319831601610af257610aed8161161f565b610d46565b60408051808201909152601b81527f7472616e736665724552433230416c6c496e2861646472657373290000000000602090910152635cf59cb760e11b6001600160e01b0319831601610b4857610aed81611b21565b60408051808201909152601881527f6465706f73697428616464726573732c6164647265737329000000000000000060209091015262d3ec1f60e31b6001600160e01b0319831601610b9d57610aed81612050565b60408051808201909152601981527f776974686472617728616464726573732c6164647265737329000000000000006020909101526306bf1c7b60e01b6001600160e01b0319831601610bf357610aed8161255f565b610c1a60405180606001604052806027815260200161372360279139805160209091012090565b6001600160e01b031916826001600160e01b03191603610c3d57610aed8161189d565b610c646040518060600160405280602881526020016136fb60289139805160209091012090565b6001600160e01b031916826001600160e01b03191603610c8757610aed8161189d565b60408051808201909152601e81527f666c61736853776170416c6c4f75742875696e743235362c627974657329000060209091015263d2b3d16560e01b6001600160e01b0319831601610cdd57610aed81611558565b60405162461bcd60e51b815260206004820152603360248201527f76616c69646174655f3164656c74614d756c746963616c6c3a20556e6b6e6f776044820152723710333ab731ba34b7b71039b2b632b1ba37b960691b60648201526084015b60405180910390fd5b505050600101610a30565b505050565b610d5e612696565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f52ae88b092de36f87fb43fe794eb1381023b9c1bce563a871154022c63dce3429190a35050565b600081806020019051810190610dc49190613056565b509050610dee816001600160a01b031660009081526007602052604090205460ff16151560011490565b610e605760405162461bcd60e51b815260206004820152603c60248201527f76616c69646174655f7472616e736665723a205265636569766572206164647260448201527f657373206e6f742077686974656c6973746564206279204775617264000000006064820152608401610d3d565b5050565b6000805b610e718361281f565b5092509050610e7f8261210b565b610e9b5760405162461bcd60e51b8152600401610d3d90613084565b610ea48161210b565b610ec05760405162461bcd60e51b8152600401610d3d90613084565b610ec98361285b565b15610d5157610ed783612895565b9250610e68565b610ee66119ad565b6001600160a01b0316846001600160a01b0316031561146357610f26846001600160a01b031660009081526005602052604090205460ff16151560011490565b610f725760405162461bcd60e51b815260206004820181905260248201527f76616c696461746543616c6c3a2053656e646572206e6f7420616c6c6f7765646044820152606401610d3d565b6000610f8160048284866130cc565b610f8a916130f6565b9050366000610f9c84600481886130cc565b91509150610faa8684611902565b6110025760405162461bcd60e51b815260206004820152602360248201527f76616c696461746543616c6c3a2043616c6c2073697465206e6f7420616c6c6f6044820152621dd95960ea1b6064820152608401610d3d565b61102960405180608001604052806043815260200161361c60439139805160209091012090565b6001600160e01b031916836001600160e01b031916036110875761108282828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506119bc92505050565b61145f565b6110ae6040518060600160405280603381526020016136c860339139805160209091012090565b6001600160e01b031916836001600160e01b031916036111075761108282828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061230a92505050565b6040805180820190915260128152716d756c746963616c6c2862797465735b5d2960701b602090910152630a6d35e560e31b6001600160e01b03198416016111885761108282828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610a1592505050565b6040805180820190915260198152787472616e7366657228616464726573732c75696e743235362960381b6020909101526356fa634560e01b6001600160e01b03198416016112105761108282828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610dae92505050565b604080518082019091526018815277617070726f766528616464726573732c75696e743235362960401b60209091015263f6a1584d60e01b6001600160e01b03198416016112975761108282828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061157a92505050565b6112be6040518060600160405280602281526020016136a660229139805160209091012090565b6001600160e01b031916836001600160e01b031916036113175761108282828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611c4792505050565b61133e60405180606001604052806026815260200161365f60269139805160209091012090565b6001600160e01b031916836001600160e01b031916036113975761108282828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506121b592505050565b6113be60405180606001604052806021815260200161368560219139805160209091012090565b6001600160e01b031916836001600160e01b031916036114175761108282828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506117f592505050565b60405162461bcd60e51b815260206004820152601960248201527f556e6b6e6f776e2066756e6374696f6e2073656c6563746f72000000000000006044820152606401610d3d565b5050505b50505050565b611471612696565b6001600160a01b03831660009081526006602052604090819020805460ff19169055517f4e13b11ab98e672bd78295ef9cebe764dc617f95decf47d842c25b83abc0c72490610a0890859085908590612e57565b6114f7836114f06040518060600160405280603381526020016136c860339139805160209091012090565b8484611700565b611522836114f060405180606001604052806034815260200161374a60349139805160209091012090565b610d51838383611d07565b611522836114f060405180608001604052806043815260200161361c60439139805160209091012090565b60008180602001905181019061156e919061312e565b915050610e6081611d66565b6000818060200190518101906115909190613056565b5090506115ba816001600160a01b031660009081526008602052604090205460ff16151560011490565b610e605760405162461bcd60e51b815260206004820152603060248201527f76616c69646174655f617070726f76653a20417070726f76652061646472657360448201526f0e640c8decae640dcdee840dac2e8c6d60831b6064820152608401610d3d565b6000818060200190518101906116359190613056565b5090506116418161210b565b610e605760405162461bcd60e51b815260206004820152602b60248201527f76616c69646174655f7472616e736665724552433230496e3a20546f6b656e2060448201526a1b9bdd08185b1b1bddd95960aa1b6064820152608401610d3d565b6116a9612696565b6001600160a01b03831660009081526007602052604090819020805460ff19166001179055517f3562181221a42a19ddd03a82dfe06acab1905ceb65cdaf7d86a1d9fec664355290610a0890859085908590612e57565b611708612696565b6001600160a01b03841660009081526002602090815260408083206001600160e01b0319871684529091528120805460ff19166001179055600380549161174e83613175565b91905055507fef729aaa41b9fd994f9ff7c1960df214a84f722002e6cfbea31799cd0873a3ef84848484604051611788949392919061318e565b60405180910390a150505050565b61179e612696565b6001600160a01b03831660009081526009602052604090819020805460ff19166001179055517f628a44970c0e450415e3ae74334ea44f3307b74dbf677a1371190242bf2f358990610a0890859085908590612e57565b6000808280602001905181019061180c91906131cd565b925050915061181a8261210b565b6118365760405162461bcd60e51b8152600401610d3d90613210565b61183f8161212e565b610d515760405162461bcd60e51b815260206004820152602960248201527f52656365697665722061646472657373206e6f742077686974656c697374656460448201526808189e4811dd585c9960ba1b6064820152608401610d3d565b6000818060200190518101906118b3919061323b565b92505050610e6081611d66565b61146384848484610ede565b6118f7836114f06040518060600160405280602281526020016136a660229139805160209091012090565b610d51838383611b5e565b6001600160a01b03821660009081526002602090815260408083206001600160e01b03198516845290915290205460ff165b92915050565b611942612696565b61194c60006128c6565b565b611956612696565b6001600160a01b03831660009081526006602052604090819020805460ff19166001179055517f62dd88c5ecfa60713a657640ebec4de26fc1aefa4afdb24e6d15a124fce7277990610a0890859085908590612e57565b60006119b7611c19565b905090565b600080828060200190518101906119d3919061328b565b509350935050506119e38161212e565b611a6a5760405162461bcd60e51b815260206004820152604c60248201527f76616c69646174655f737761704578616374546f6b656e73466f72546f6b656e60448201527f733a2052656365697665722061646472657373206e6f742077686974656c697360648201526b1d195908189e4811dd585c9960a21b608482015260a401610d3d565b6000805b8351811015611ac057838181518110611a8957611a89612fce565b60200260200101519150611a9c8261210b565b611ab85760405162461bcd60e51b8152600401610d3d90613210565b600101611a6e565b5050505050565b611acf612696565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f90cc2f570a6eb594b1580ea3e41247d2d73a55281889e86bd4ec2fc29c7e62d690600090a35050565b600081806020019051810190611b37919061335b565b9050611b428161210b565b610e605760405162461bcd60e51b8152600401610d3d90613378565b611b66612696565b6001600160a01b03831660009081526004602052604090819020805460ff19166001179055517fad90e2570fc4fe9f7437be3188d5c791c6662892d31731992aa88f5c8762198390610a0890859085908590612e57565b611bc5612696565b6001600160a01b03831660009081526007602052604090819020805460ff19169055517f1212f8ddb39de8e1a339460c95752d61100723efeaa34ccd94bca94393f4093890610a0890859085908590612e57565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b600081806020019051810190611c5d9190613056565b509050611c87816001600160a01b031660009081526009602052604090205460ff16151560011490565b610e605760405162461bcd60e51b815260206004820152604560248201527f76616c69646174655f617070726f766544656c65676174696f6e3a204170707260448201527f6f76652064656c65676174696f6e206164647265737320646f6573206e6f74206064820152640dac2e8c6d60db1b608482015260a401610d3d565b611d0f612696565b6001600160a01b03831660009081526008602052604090819020805460ff19166001179055517f628a44970c0e450415e3ae74334ea44f3307b74dbf677a1371190242bf2f358990610a0890859085908590612e57565b6000805b611d75836000612937565b9150611da4600180611d89600360146133c6565b611d9391906133c6565b611d9d91906133c6565b8490612937565b9050611daf8261210b565b611dcb5760405162461bcd60e51b8152600401610d3d906133d9565b611dd48161210b565b611df05760405162461bcd60e51b8152600401610d3d906133d9565b600180611dff600360146133c6565b611e0991906133c6565b611e1391906133c6565b6014600180611e236003846133c6565b611e2d91906133c6565b611e3791906133c6565b611e4191906133c6565b611e4b91906133c6565b835110610d5157611ea7600180611e64600360146133c6565b611e6e91906133c6565b611e7891906133c6565b600180611e87600360146133c6565b611e9191906133c6565b611e9b91906133c6565b8551610a8f9190613038565b9250611d6a565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b0316600081158015611ef35750825b90506000826001600160401b03166001148015611f0f5750303b155b905081158015611f1d575080155b15611f3b5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611f6557845460ff60401b1916600160401b1785555b611f6e336129eb565b60008087806020019051810190611f85919061341e565b91509150611f9281610d56565b611f9b81611ac7565b611fa482612521565b50508315611fec57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b611ffc612696565b6001600160a01b03831660009081526005602052604090819020805460ff19169055517f3097928509c53a2dab9500431201d82b0d756e8f890fd01f4ae6b33b45687ea490610a0890859085908590612e57565b60008082806020019051810190612067919061341e565b915091506120748261210b565b6120905760405162461bcd60e51b8152600401610d3d90613378565b6120998161212e565b610d515760405162461bcd60e51b815260206004820152603b60248201527f76616c69646174655f6465706f7369743a20526563656976657220616464726560448201527f7373206e6f742077686974656c697374656420627920477561726400000000006064820152608401610d3d565b6001600160a01b031660009081526004602052604090205460ff16151560011490565b6001600160a01b031660009081526006602052604090205460ff16151560011490565b6000606061216133868686610ede565b6121a385600086868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092018290525092506129fc915050565b909250905081611ac057805160208201fd5b6000818060200190518101906121cb9190613458565b50505090506121d98161210b565b610e605760405162461bcd60e51b8152600401610d3d90613210565b6121fd612696565b6001600160a01b03841660009081526002602090815260408083206001600160e01b03198716845290915290819020805460ff19169055517f37ea10f2d08f5a9803dfcd5abf3cfc7b0d6fcdf5fcbc36be9ea5a0b53d9a4d9b9061178890869086908690869061318e565b60008180602001905181019061227e919061353a565b905061228d816020015161212e565b6122ff5760405162461bcd60e51b815260206004820152603f60248201527f76616c69646174655f65786163744f75747075743a205265636569766572206160448201527f646472657373206e6f742077686974656c6973746564206279204775617264006064820152608401610d3d565b8051610e6090610e64565b600081806020019051810190612320919061353a565b905061232f816020015161212e565b6122ff5760405162461bcd60e51b815260206004820152603e60248201527f76616c69646174655f6578616374496e7075743a20526563656976657220616460448201527f6472657373206e6f742077686974656c697374656420627920477561726400006064820152608401610d3d565b6040805180820190915260198152787472616e7366657228616464726573732c75696e743235362960381b6020909101526123fc837fa9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b6114f0565b604080518082019091526018815277617070726f766528616464726573732c75696e743235362960401b6020909101526118f7837f095ea7b334ae44009aa867bfb386f5c3b4b443ac6f0ee573fa91c4608fbadfba6114f0565b612481836114f060405180606001604052806026815260200161365f60269139805160209091012090565b611522836114f060405180606001604052806021815260200161368560219139805160209091012090565b6040805180820190915260128152716d756c746963616c6c2862797465735b5d2960701b602090910152612500847fac9650d882acfa253cba1ed543b9ff47351da99c3bd00a89625e5cdb2099009c6114f0565b61250b848383611d07565b612516838383611d07565b611463848383611796565b612529612696565b6001600160a01b03811661255357604051631e4fbdf760e01b815260006004820152602401610d3d565b61255c816128c6565b50565b60008082806020019051810190612576919061341e565b915091506125838261210b565b6120905760405162461bcd60e51b8152602060048201526024808201527f76616c69646174655f77697468647261773a20546f6b656e206e6f7420616c6c6044820152631bddd95960e21b6064820152608401610d3d565b6125e3612696565b6001600160a01b03831660009081526004602052604090819020805460ff19169055517f9ca3f065622f5f03f32b7157677a0e420c3a36ab45fd49f256ffebce3e31058790610a0890859085908590612e57565b61263f612696565b6001600160a01b03831660009081526005602052604090819020805460ff19166001179055517fa8f9caaf4861720900294428e4ff34d37070c37afd26d96e6e4da75326d2c3ad90610a0890859085908590612e57565b3361269f611c19565b6001600160a01b03161461194c5760405163118cdaa760e01b8152336004820152602401610d3d565b6060816126d681601f6133c6565b10156127155760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606401610d3d565b8261272083826133c6565b101561275f5760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606401610d3d565b61276982846133c6565b845110156127ad5760405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b6044820152606401610d3d565b6060821580156127cc5760405191506000825260208201604052612816565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156128055780518352602092830192016127ed565b5050858452601f01601f1916604052505b50949350505050565b6000808061282d8482612937565b925061283a846014612a8c565b905061285261284b600360146133c6565b8590612937565b91509193909250565b6000612869600360146133c6565b60146128766003826133c6565b61288091906133c6565b61288a91906133c6565b825110159050919050565b60606119346128a6600360146133c6565b6128b2600360146133c6565b84516128be9190613038565b8491906126c8565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6000816129458160146133c6565b10156129885760405162461bcd60e51b8152602060048201526012602482015271746f416464726573735f6f766572666c6f7760701b6044820152606401610d3d565b6129938260146133c6565b835110156129db5760405162461bcd60e51b8152602060048201526015602482015274746f416464726573735f6f75744f66426f756e647360581b6044820152606401610d3d565b500160200151600160601b900490565b6129f3612b37565b61255c81612b80565b600154604051635229073f60e01b81526000916060916001600160a01b0390911690635229073f90612a3890899089908990899060040161356e565b6000604051808303816000875af1158015612a57573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612a7f91908101906135dc565b9150915094509492505050565b600081612a9a8160036133c6565b1015612adc5760405162461bcd60e51b8152602060048201526011602482015270746f55696e7432345f6f766572666c6f7760781b6044820152606401610d3d565b612ae78260036133c6565b83511015612b2e5760405162461bcd60e51b8152602060048201526014602482015273746f55696e7432345f6f75744f66426f756e647360601b6044820152606401610d3d565b50016003015190565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661194c57604051631afcd79f60e31b815260040160405180910390fd5b612529612b37565b6001600160a01b038116811461255c57600080fd5b60008083601f840112612baf57600080fd5b5081356001600160401b03811115612bc657600080fd5b602083019150836020828501011115612bde57600080fd5b9250929050565b600080600060408486031215612bfa57600080fd5b8335612c0581612b88565b925060208401356001600160401b03811115612c2057600080fd5b612c2c86828701612b9d565b9497909650939450505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715612c7757612c77612c39565b604052919050565b60006001600160401b03821115612c9857612c98612c39565b50601f01601f191660200190565b600060208284031215612cb857600080fd5b81356001600160401b03811115612cce57600080fd5b8201601f81018413612cdf57600080fd5b8035612cf2612ced82612c7f565b612c4f565b818152856020838501011115612d0757600080fd5b81602084016020830137600091810160200191909152949350505050565b600060208284031215612d3757600080fd5b8135612d4281612b88565b9392505050565b60008060008060608587031215612d5f57600080fd5b8435612d6a81612b88565b93506020850135612d7a81612b88565b925060408501356001600160401b03811115612d9557600080fd5b612da187828801612b9d565b95989497509550505050565b80356001600160e01b031981168114612dc557600080fd5b919050565b60008060008060608587031215612de057600080fd5b8435612deb81612b88565b9350612d7a60208601612dad565b60008060408385031215612e0c57600080fd5b8235612e1781612b88565b9150612e2560208401612dad565b90509250929050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b0384168152604060208201819052600090612e7c9083018486612e2e565b95945050505050565b60006001600160401b03821115612e9e57612e9e612c39565b5060051b60200190565b60005b83811015612ec3578181015183820152602001612eab565b50506000910152565b600082601f830112612edd57600080fd5b8151612eeb612ced82612c7f565b818152846020838601011115612f0057600080fd5b612f11826020830160208701612ea8565b949350505050565b600060208284031215612f2b57600080fd5b81516001600160401b03811115612f4157600080fd5b8201601f81018413612f5257600080fd5b8051612f60612ced82612e85565b8082825260208201915060208360051b850101925086831115612f8257600080fd5b602084015b83811015612fc35780516001600160401b03811115612fa557600080fd5b612fb489602083890101612ecc565b84525060209283019201612f87565b509695505050505050565b634e487b7160e01b600052603260045260246000fd5b805160208201516001600160e01b031981169190600482101561301b576001600160e01b0319600483900360031b81901b82161692505b5050919050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561193457611934613022565b8051612dc581612b88565b6000806040838503121561306957600080fd5b825161307481612b88565b6020939093015192949293505050565b60208082526028908201527f76616c6964617465556e69737761705633506174683a20546f6b656e206e6f7460408201526708185b1b1bddd95960c21b606082015260800190565b600080858511156130dc57600080fd5b838611156130e957600080fd5b5050820193919092039150565b80356001600160e01b03198116906004841015613127576001600160e01b0319600485900360031b81901b82161691505b5092915050565b6000806040838503121561314157600080fd5b825160208401519092506001600160401b0381111561315f57600080fd5b61316b85828601612ecc565b9150509250929050565b60006001820161318757613187613022565b5060010190565b6001600160a01b03851681526001600160e01b0319841660208201526060604082018190526000906131c39083018486612e2e565b9695505050505050565b6000806000606084860312156131e257600080fd5b83516131ed81612b88565b60208501516040860151919450925061320581612b88565b809150509250925092565b602080825260119082015270151bdad95b881b9bdd08185b1b1bddd959607a1b604082015260600190565b60008060006060848603121561325057600080fd5b83516020850151604086015191945092506001600160401b0381111561327557600080fd5b61328186828701612ecc565b9150509250925092565b600080600080600060a086880312156132a357600080fd5b85516020870151604088015191965094506001600160401b038111156132c857600080fd5b8601601f810188136132d957600080fd5b80516132e7612ced82612e85565b8082825260208201915060208360051b85010192508a83111561330957600080fd5b6020840193505b8284101561333457835161332381612b88565b825260209384019390910190613310565b9550613346925050506060870161304b565b60809690960151949793965091949392915050565b60006020828403121561336d57600080fd5b8151612d4281612b88565b6020808252602e908201527f76616c69646174655f7472616e736665724552433230416c6c496e3a20546f6b60408201526d195b881b9bdd08185b1b1bddd95960921b606082015260800190565b8082018082111561193457611934613022565b60208082526025908201527f76616c69646174653164656c7461506174683a20546f6b656e206e6f7420616c6040820152641b1bddd95960da1b606082015260800190565b6000806040838503121561343157600080fd5b825161343c81612b88565b602084015190925061344d81612b88565b809150509250929050565b6000806000806080858703121561346e57600080fd5b845161347981612b88565b60208601516040870151919550935061349181612b88565b6060959095015193969295505050565b600060a082840312156134b357600080fd5b60405160a081016001600160401b03811182821017156134d5576134d5612c39565b806040525080915082516001600160401b038111156134f357600080fd5b6134ff85828601612ecc565b825250602083015161351081612b88565b60208201526040838101519082015260608084015190820152608092830151920191909152919050565b60006020828403121561354c57600080fd5b81516001600160401b0381111561356257600080fd5b612f11848285016134a1565b60018060a01b038516815283602082015260806040820152600083518060808401526135a18160a0850160208801612ea8565b601f01601f1916820160a0019050600283106135cd57634e487b7160e01b600052602160045260246000fd5b82606083015295945050505050565b600080604083850312156135ef57600080fd5b825180151581146135ff57600080fd5b60208401519092506001600160401b0381111561315f57600080fdfe737761704578616374546f6b656e73466f72546f6b656e732875696e743235362c75696e743235362c616464726573735b5d2c616464726573732c75696e7432353629737570706c7928616464726573732c75696e743235362c616464726573732c75696e74313629776974686472617728616464726573732c75696e743235362c6164647265737329617070726f766544656c65676174696f6e28616464726573732c75696e74323536296578616374496e707574282862797465732c616464726573732c75696e743235362c75696e743235362c75696e743235362929666c6173685377617045786163744f75742875696e743235362c75696e743235362c627974657329666c617368537761704578616374496e2875696e743235362c75696e743235362c62797465732965786163744f7574707574282862797465732c616464726573732c75696e743235362c75696e743235362c75696e743235362929a2646970667358221220e57df6ec2f8bed9582c2ebb689735b1f77e8164ce6e68394e4e0543b95769a0064736f6c634300081a0033","sourceMap":"1060:1936:14:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6204:226:0;;;;;;:::i;:::-;;:::i;:::-;;14134:1680;;;;;;:::i;:::-;;:::i;2547:79::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;2774:14:15;;2767:22;2749:41;;2737:2;2722:18;2547:79:0;;;;;;;;980:162:11;;;;;;:::i;:::-;;:::i;8416:248:0:-;;;;;;:::i;:::-;;:::i;13203:539::-;;;;;;:::i;:::-;;:::i;7956:152::-;;;;;;:::i;:::-;-1:-1:-1;;;;;8056:37:0;8033:4;8056:37;;;:27;:37;;;;;;;;:45;;:37;:45;;7956:152;9926:1751;;;;;;:::i;:::-;;:::i;5318:184::-;;;;;;:::i;:::-;;:::i;8114:172::-;;;;;;:::i;:::-;-1:-1:-1;;;;;8224:47:0;8201:4;8224:47;;;:37;:47;;;;;;;;:55;;:47;:55;;8114:172;13748:351;;;;;;:::i;:::-;;:::i;2453:65::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;12230:260;;;;;;:::i;:::-;;:::i;18721:183::-;;;;;;:::i;:::-;;:::i;2765:89::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;8670:235;;;;;;:::i;:::-;;:::i;16005:234::-;;;;;;:::i;:::-;;:::i;5508:226::-;;;;;;:::i;:::-;;:::i;500:21:11:-;;;;;-1:-1:-1;;;;;500:21:11;;;;;;-1:-1:-1;;;;;3656:32:15;;;3638:51;;3626:2;3611:18;500:21:11;3492:203:15;4305:240:0;;;;;;:::i;:::-;;:::i;6436:246::-;;;;;;:::i;:::-;;:::i;21410:308::-;;;;;;:::i;:::-;;:::i;18334:196::-;;;;;;:::i;:::-;;:::i;9709:211::-;;;;;;:::i;:::-;;:::i;9468:213::-;;;;;;:::i;:::-;;:::i;7331:145::-;;;;;;:::i;:::-;;:::i;3155:101:7:-;;;:::i;5128:184:0:-;;;;;;:::i;:::-;;:::i;1421:102:14:-;;;:::i;11716:508:0:-;;;;;;:::i;:::-;;:::i;1259:162:11:-;;;;;;:::i;:::-;;:::i;16430:229:0:-;;;;;;:::i;:::-;;:::i;6930:166::-;;;;;;:::i;:::-;;:::i;5740:226::-;;;;;;:::i;:::-;;:::i;2441:144:7:-;;;:::i;8911:276:0:-;;;;;;:::i;:::-;;:::i;5972:226::-;;;;;;:::i;:::-;;:::i;19445:729::-;;;;;;:::i;:::-;;:::i;2235:59::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;1760:302:14;;;;;;:::i;:::-;;:::i;4950:172:0:-;;;;;;:::i;:::-;;:::i;1928:91::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;2140:25;;;;;;;;;4971::15;;;4959:2;4944:18;2140:25:0;4825:177:15;7482:122:0;;;;;;:::i;:::-;-1:-1:-1;;;;;7567:22:0;7544:4;7567:22;;;:14;:22;;;;;;;;:30;;:22;:30;;7482:122;16854:368;;;;;;:::i;:::-;;:::i;8292:118::-;;;;;;:::i;:::-;;:::i;7662:130::-;;;;;;:::i;:::-;;:::i;2332:662:14:-;;;;;;:::i;:::-;;:::i;588:21:11:-;;;;;-1:-1:-1;;;;;588:21:11;;;20941:311:0;;;;;;:::i;:::-;;:::i;4551:215::-;;;;;;:::i;:::-;;:::i;12864:333::-;;;;;;:::i;:::-;;:::i;2656:79::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;12529:329;;;;;;:::i;:::-;;:::i;9193:269::-;;;;;;:::i;:::-;;:::i;7798:152::-;;;;;;:::i;:::-;-1:-1:-1;;;;;7898:37:0;7875:4;7898:37;;;:27;:37;;;;;;;;:45;;:37;:45;;7798:152;21724:339;;;;;;:::i;:::-;;:::i;20180:603::-;;;;;;:::i;:::-;;:::i;3405:215:7:-;;;;;;:::i;:::-;;:::i;17398:359:0:-;;;;;;:::i;:::-;;:::i;7102:166::-;;;;;;:::i;:::-;;:::i;4772:172::-;;;;;;:::i;:::-;;:::i;2343:61::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;4216:83;;;4291:1;6391:36:15;;6379:2;6364:18;4216:83:0;6249:184:15;6204:226:0;1328:13:14;:11;:13::i;:::-;-1:-1:-1;;;;;6322:40:0;::::1;;::::0;;;:27:::1;:40;::::0;;;;;;6315:47;;-1:-1:-1;;6315:47:0::1;::::0;;6377:46;::::1;::::0;::::1;::::0;6350:11;;6417:5;;;;6377:46:::1;:::i;:::-;;;;;;;;6204:226:::0;;;:::o;14134:1680::-;14214:22;14251:8;14240:31;;;;;;;;;;;;:::i;:::-;14213:58;;14338:6;14333:1475;14350:7;:14;14346:1;:18;14333:1475;;;14385:33;14421:7;14429:1;14421:10;;;;;;;;:::i;:::-;;;;;;;14385:46;;14506:15;14531:32;14558:1;14561;14531:20;:26;;:32;;;;;:::i;:::-;14524:40;;;:::i;:::-;14506:58;;14578:24;14605:62;14632:1;14665;14635:20;:27;:31;;;;:::i;:::-;14605:20;;:62;:26;:62::i;:::-;14736:47;;;;;;;;;;;;;;;;;14578:89;-1:-1:-1;;;;;;;;;;14724:59:0;;;14720:1078;;14803:37;14828:11;14803:24;:37::i;:::-;14720:1078;;;14877:42;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;14865:54:0;;;14861:937;;14939:40;14967:11;14939:27;:40::i;14861:937::-;15016:39;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;15004:51:0;;;15000:798;;15075:35;15098:11;15075:22;:35::i;15000:798::-;15147:40;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;15135:52:0;;;15131:667;;15207:36;15231:11;15207:23;:36::i;15131:667::-;15280:54;;;;;;;;;;;;;;;;;;4058:23;;;;;;;;3899:190;15280:54;-1:-1:-1;;;;;15268:66:0;;:8;-1:-1:-1;;;;;15268:66:0;;;15264:534;;15354:39;15381:11;15354:26;:39::i;15264:534::-;15430:55;;;;;;;;;;;;;;;;;;4058:23;;;;;;;;3899:190;15430:55;-1:-1:-1;;;;;15418:67:0;;:8;-1:-1:-1;;;;;15418:67:0;;;15414:384;;15505:39;15532:11;15505:26;:39::i;15414:384::-;15581:45;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;15569:57:0;;;15565:233;;15646:37;15671:11;15646:24;:37::i;15565:233::-;15722:61;;-1:-1:-1;;;15722:61:0;;10001:2:15;15722:61:0;;;9983:21:15;10040:2;10020:18;;;10013:30;10079:34;10059:18;;;10052:62;-1:-1:-1;;;10130:18:15;;;10123:49;10189:19;;15722:61:0;;;;;;;;15565:233;-1:-1:-1;;;14366:3:0;;14333:1475;;;;14203:1611;14134:1680;:::o;980:162:11:-;2334:13:7;:11;:13::i;:::-;1039:22:11::1;1064:6:::0;;-1:-1:-1;;;;;1076:16:11;;::::1;-1:-1:-1::0;;;;;;1076:16:11;::::1;::::0;::::1;::::0;;1103:34:::1;::::0;1064:6;;;::::1;::::0;;;1103:34:::1;::::0;1039:22;1103:34:::1;1033:109;980:162:::0;:::o;8416:248:0:-;8489:10;8516:8;8505:37;;;;;;;;;;;;:::i;:::-;8488:54;;;8560:32;8589:2;-1:-1:-1;;;;;7898:37:0;7875:4;7898:37;;;:27;:37;;;;;;;;:45;;:37;:45;;7798:152;8560:32;8552:105;;;;-1:-1:-1;;;8552:105:0;;10949:2:15;8552:105:0;;;10931:21:15;10988:2;10968:18;;;10961:30;11027:34;11007:18;;;11000:62;11098:30;11078:18;;;11071:58;11146:19;;8552:105:0;10747:424:15;8552:105:0;8478:186;8416:248;:::o;13203:539::-;13275:15;13300:16;13327:409;13378:22;:4;:20;:22::i;:::-;-1:-1:-1;13354:46:0;-1:-1:-1;13354:46:0;-1:-1:-1;13423:23:0;13354:46;13423:14;:23::i;:::-;13415:76;;;;-1:-1:-1;;;13415:76:0;;;;;;;:::i;:::-;13513:24;13528:8;13513:14;:24::i;:::-;13505:77;;;;-1:-1:-1;;;13505:77:0;;;;;;;:::i;:::-;13601:23;:4;:21;:23::i;:::-;13597:129;;;13651:16;:4;:14;:16::i;:::-;13644:23;;13327:409;;9926:1751;10092:22;:20;:22::i;:::-;-1:-1:-1;;;;;10082:32:0;:6;-1:-1:-1;;;;;10082:32:0;;10079:125;10187:7;10079:125;10222:23;10238:6;-1:-1:-1;;;;;7567:22:0;7544:4;7567:22;;;:14;:22;;;;;;;;:30;;:22;:30;;7482:122;10222:23;10214:68;;;;-1:-1:-1;;;10214:68:0;;11787:2:15;10214:68:0;;;11769:21:15;;;11806:18;;;11799:30;11865:34;11845:18;;;11838:62;11917:18;;10214:68:0;11585:356:15;10214:68:0;10348:15;10373:24;10395:1;10348:15;10373:20;;:24;:::i;:::-;10366:32;;;:::i;:::-;10348:50;-1:-1:-1;10408:23:0;;10434:24;:20;10455:1;10434:20;;:24;:::i;:::-;10408:50;;;;10476:35;10494:6;10502:8;10476:17;:35::i;:::-;10468:83;;;;-1:-1:-1;;;10468:83:0;;12827:2:15;10468:83:0;;;12809:21:15;12866:2;12846:18;;;12839:30;12905:34;12885:18;;;12878:62;-1:-1:-1;;;12956:18:15;;;12949:33;12999:19;;10468:83:0;12625:399:15;10468:83:0;10577:82;;;;;;;;;;;;;;;;;;4058:23;;;;;;;;3899:190;10577:82;-1:-1:-1;;;;;10565:94:0;;:8;-1:-1:-1;;;;;10565:94:0;;;10562:1109;;10675:43;10709:8;;10675:43;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;10675:33:0;;-1:-1:-1;;;10675:43:0:i;:::-;10562:1109;;;10750:66;;;;;;;;;;;;;;;;;;4058:23;;;;;;;;3899:190;10750:66;-1:-1:-1;;;;;10738:78:0;;:8;-1:-1:-1;;;;;10738:78:0;;;10735:936;;10832:29;10852:8;;10832:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;10832:19:0;;-1:-1:-1;;;10832:29:0:i;10735:936::-;10893:33;;;;;;;;;;;;-1:-1:-1;;;10893:33:0;;;;;-1:-1:-1;;;;;;;;;10881:45:0;;;10878:793;;10942:34;10967:8;;10942:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;10942:24:0;;-1:-1:-1;;;10942:34:0:i;10878:793::-;11008:40;;;;;;;;;;;;-1:-1:-1;;;11008:40:0;;;;;-1:-1:-1;;;;;;;;;10996:52:0;;;10993:678;;11064:27;11082:8;;11064:27;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;11064:17:0;;-1:-1:-1;;;11064:27:0:i;10993:678::-;11123:39;;;;;;;;;;;;-1:-1:-1;;;11123:39:0;;;;;-1:-1:-1;;;;;;;;;11111:51:0;;;11108:563;;11178:26;11195:8;;11178:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;11178:16:0;;-1:-1:-1;;;11178:26:0:i;11108:563::-;11236:49;;;;;;;;;;;;;;;;;;4058:23;;;;;;;;3899:190;11236:49;-1:-1:-1;;;;;11224:61:0;;:8;-1:-1:-1;;;;;11224:61:0;;;11221:450;;11301:36;11328:8;;11301:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;11301:26:0;;-1:-1:-1;;;11301:36:0:i;11221:450::-;11369:53;;;;;;;;;;;;;;;;;;4058:23;;;;;;;;3899:190;11369:53;-1:-1:-1;;;;;11357:65:0;;:8;-1:-1:-1;;;;;11357:65:0;;;11354:317;;11438:29;11458:8;;11438:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;11438:19:0;;-1:-1:-1;;;11438:29:0:i;11354:317::-;11499:48;;;;;;;;;;;;;;;;;;4058:23;;;;;;;;3899:190;11499:48;-1:-1:-1;;;;;11487:60:0;;:8;-1:-1:-1;;;;;11487:60:0;;;11484:187;;11563:31;11585:8;;11563:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;11563:21:0;;-1:-1:-1;;;11563:31:0:i;11484:187::-;11625:35;;-1:-1:-1;;;11625:35:0;;13231:2:15;11625:35:0;;;13213:21:15;13270:2;13250:18;;;13243:30;13309:27;13289:18;;;13282:55;13354:18;;11625:35:0;13029:349:15;11484:187:0;10068:1609;;;9926:1751;;;;;:::o;5318:184::-;1328:13:14;:11;:13::i;:::-;-1:-1:-1;;;;;5422:26:0;::::1;;::::0;;;:16:::1;:26;::::0;;;;;;5415:33;;-1:-1:-1;;5415:33:0::1;::::0;;5463:32;::::1;::::0;::::1;::::0;5439:8;;5489:5;;;;5463:32:::1;:::i;13748:351::-:0;13840:96;13854:6;13862:66;;;;;;;;;;;;;;;;;;4058:23;;;;;;;;3899:190;13862:66;13930:5;;13840:13;:96::i;:::-;13946:97;13960:6;13968:67;;;;;;;;;;;;;;;;;;4058:23;;;;;;;;3899:190;13946:97;14053:39;14078:6;14086:5;;14053:24;:39::i;12230:260::-;12322:112;12336:6;12344:82;;;;;;;;;;;;;;;;;;4058:23;;;;;;;;3899:190;18721:183;18803:17;18835:8;18824:38;;;;;;;;;;;;:::i;:::-;18800:62;;;18873:24;18892:4;18873:18;:24::i;8670:235::-;8742:10;8769:8;8758:37;;;;;;;;;;;;:::i;:::-;8741:54;;;8813:32;8842:2;-1:-1:-1;;;;;8056:37:0;8033:4;8056:37;;;:27;:37;;;;;;;;:45;;:37;:45;;7956:152;8813:32;8805:93;;;;-1:-1:-1;;;8805:93:0;;14032:2:15;8805:93:0;;;14014:21:15;14071:2;14051:18;;;14044:30;14110:34;14090:18;;;14083:62;-1:-1:-1;;;14161:18:15;;;14154:46;14217:19;;8805:93:0;13830:412:15;16005:234:0;16085:13;16115:8;16104:40;;;;;;;;;;;;:::i;:::-;16084:60;;;16163:21;16178:5;16163:14;:21::i;:::-;16155:77;;;;-1:-1:-1;;;16155:77:0;;14449:2:15;16155:77:0;;;14431:21:15;14488:2;14468:18;;;14461:30;14527:34;14507:18;;;14500:62;-1:-1:-1;;;14578:18:15;;;14571:41;14629:19;;16155:77:0;14247:407:15;5508:226:0;1328:13:14;:11;:13::i;:::-;-1:-1:-1;;;;;5618:40:0;::::1;;::::0;;;:27:::1;:40;::::0;;;;;;:47;;-1:-1:-1;;5618:47:0::1;5661:4;5618:47;::::0;;5680;::::1;::::0;::::1;::::0;5646:11;;5721:5;;;;5680:47:::1;:::i;4305:240::-:0;1328:13:14;:11;:13::i;:::-;-1:-1:-1;;;;;4416:24:0;::::1;;::::0;;;:16:::1;:24;::::0;;;;;;;-1:-1:-1;;;;;;4416:34:0;::::1;::::0;;;;;;;:41;;-1:-1:-1;;4416:41:0::1;4453:4;4416:41;::::0;;4467:13:::1;:15:::0;;;::::1;::::0;::::1;:::i;:::-;;;;;;4497:41;4514:6;4522:8;4532:5;;4497:41;;;;;;;;;:::i;:::-;;;;;;;;4305:240:::0;;;;:::o;6436:246::-;1328:13:14;:11;:13::i;:::-;-1:-1:-1;;;;;6556:50:0;::::1;;::::0;;;:37:::1;:50;::::0;;;;;;:57;;-1:-1:-1;;6556:57:0::1;6609:4;6556:57;::::0;;6628:47;::::1;::::0;::::1;::::0;6594:11;;6669:5;;;;6628:47:::1;:::i;21410:308::-:0;21487:13;21504:10;21529:8;21518:46;;;;;;;;;;;;:::i;:::-;21486:78;;;;;21583:21;21598:5;21583:14;:21::i;:::-;21575:51;;;;-1:-1:-1;;;21575:51:0;;;;;;;:::i;:::-;21644:21;21662:2;21644:17;:21::i;:::-;21636:75;;;;-1:-1:-1;;;21636:75:0;;16311:2:15;21636:75:0;;;16293:21:15;16350:2;16330:18;;;16323:30;16389:34;16369:18;;;16362:62;-1:-1:-1;;;16440:18:15;;;16433:39;16489:19;;21636:75:0;16109:405:15;18334:196:0;18420:17;18452:8;18441:47;;;;;;;;;;;;:::i;:::-;18415:73;;;;18499:24;18518:4;18499:18;:24::i;9709:211::-;9854:59;9876:6;9884;9892:20;;9854:21;:59::i;9468:213::-;9562:78;9576:5;9583:49;;;;;;;;;;;;;;;;;;4058:23;;;;;;;;3899:190;9562:78;9650:24;9661:5;9668;;9650:10;:24::i;7331:145::-;-1:-1:-1;;;;;7435:24:0;;7412:4;7435:24;;;:16;:24;;;;;;;;-1:-1:-1;;;;;;7435:34:0;;;;;;;;;;;;7331:145;;;;;:::o;3155:101:7:-;2334:13;:11;:13::i;:::-;3219:30:::1;3246:1;3219:18;:30::i;:::-;3155:101::o:0;5128:184:0:-;1328:13:14;:11;:13::i;:::-;-1:-1:-1;;;;;5224:26:0;::::1;;::::0;;;:16:::1;:26;::::0;;;;;;:33;;-1:-1:-1;;5224:33:0::1;5253:4;5224:33;::::0;;5272;::::1;::::0;::::1;::::0;5241:8;;5299:5;;;;5272:33:::1;:::i;1421:102:14:-:0;1483:7;1509;:5;:7::i;:::-;1502:14;;1421:102;:::o;11716:508:0:-;11809:21;11832:10;11859:8;11848:60;;;;;;;;;;;;:::i;:::-;11804:104;;;;;;;11927:21;11945:2;11927:17;:21::i;:::-;11919:110;;;;-1:-1:-1;;;11919:110:0;;18677:2:15;11919:110:0;;;18659:21:15;18716:2;18696:18;;;18689:30;18755:34;18735:18;;;18728:62;18826:34;18806:18;;;18799:62;-1:-1:-1;;;18877:19:15;;;18870:43;18930:19;;11919:110:0;18475:480:15;11919:110:0;12040:13;;12063:147;12087:4;:11;12083:1;:15;12063:147;;;12127:4;12132:1;12127:7;;;;;;;;:::i;:::-;;;;;;;12119:15;;12156:21;12171:5;12156:14;:21::i;:::-;12148:51;;;;-1:-1:-1;;;12148:51:0;;;;;;;:::i;:::-;12100:3;;12063:147;;;;11794:430;;;11716:508;:::o;1259:162:11:-;2334:13:7;:11;:13::i;:::-;1343:6:11::1;::::0;;-1:-1:-1;;;;;1355:16:11;;::::1;-1:-1:-1::0;;;;;;1355:16:11;::::1;::::0;::::1;::::0;;;1382:34:::1;::::0;1343:6;::::1;::::0;1355:16;1343:6;;1382:34:::1;::::0;1318:22:::1;::::0;1382:34:::1;1312:109;1259:162:::0;:::o;16430:229:0:-;16513:13;16541:8;16530:31;;;;;;;;;;;;:::i;:::-;16512:49;;16580:21;16595:5;16580:14;:21::i;:::-;16572:80;;;;-1:-1:-1;;;16572:80:0;;;;;;;:::i;6930:166::-;1328:13:14;:11;:13::i;:::-;-1:-1:-1;;;;;7020:20:0;::::1;;::::0;;;:13:::1;:20;::::0;;;;;;:27;;-1:-1:-1;;7020:27:0::1;7043:4;7020:27;::::0;;7062;::::1;::::0;::::1;::::0;7034:5;;7083;;;;7062:27:::1;:::i;5740:226::-:0;1328:13:14;:11;:13::i;:::-;-1:-1:-1;;;;;5858:40:0;::::1;;::::0;;;:27:::1;:40;::::0;;;;;;5851:47;;-1:-1:-1;;5851:47:0::1;::::0;;5913:46;::::1;::::0;::::1;::::0;5886:11;;5953:5;;;;5913:46:::1;:::i;2441:144:7:-:0;1313:22;2570:8;-1:-1:-1;;;;;2570:8:7;;2441:144::o;8911:276:0:-;8993:10;9020:8;9009:37;;;;;;;;;;;;:::i;:::-;8992:54;;;9064:42;9103:2;-1:-1:-1;;;;;8224:47:0;8201:4;8224:47;;;:37;:47;;;;;;;;:55;;:47;:55;;8114:172;9064:42;9056:124;;;;-1:-1:-1;;;9056:124:0;;19841:2:15;9056:124:0;;;19823:21:15;19880:2;19860:18;;;19853:30;19919:34;19899:18;;;19892:62;19990:34;19970:18;;;19963:62;-1:-1:-1;;;20041:19:15;;;20034:36;20087:19;;9056:124:0;19639:473:15;5972:226:0;1328:13:14;:11;:13::i;:::-;-1:-1:-1;;;;;6082:40:0;::::1;;::::0;;;:27:::1;:40;::::0;;;;;;:47;;-1:-1:-1;;6082:47:0::1;6125:4;6082:47;::::0;;6144;::::1;::::0;::::1;::::0;6110:11;;6185:5;;;;6144:47:::1;:::i;19445:729::-:0;19514:15;19539:16;19566:602;19603:17;:4;19618:1;19603:14;:17::i;:::-;19593:27;-1:-1:-1;19645:36:0;1032:1;;1158:29;821:1;714:2;1158:29;:::i;:::-;:49;;;;:::i;:::-;:72;;;;:::i;:::-;19645:4;;:14;:36::i;:::-;19634:47;;19704:23;19719:7;19704:14;:23::i;:::-;19696:73;;;;-1:-1:-1;;;19696:73:0;;;;;;;:::i;:::-;19791:24;19806:8;19791:14;:24::i;:::-;19783:74;;;;-1:-1:-1;;;19783:74:0;;;;;;;:::i;:::-;1032:1;;1158:29;821:1;714:2;1158:29;:::i;:::-;:49;;;;:::i;:::-;:72;;;;:::i;:::-;714:2;1032:1;;1158:29;821:1;714:2;1158:29;:::i;:::-;:49;;;;:::i;:::-;:72;;;;:::i;:::-;1330:32;;;;:::i;:::-;1507:42;;;;:::i;:::-;19955:4;:11;:49;19951:207;;20031:68;1032:1;;1158:29;821:1;714:2;1158:29;:::i;:::-;:49;;;;:::i;:::-;:72;;;;:::i;:::-;1032:1;;1158:29;821:1;714:2;1158:29;:::i;:::-;:49;;;;:::i;:::-;:72;;;;:::i;:::-;20064:4;:11;:34;;;;:::i;20031:68::-;20024:75;;19566:602;;1760:302:14;8870:21:8;4302:15;;-1:-1:-1;;;4302:15:8;;;;4301:16;;-1:-1:-1;;;;;4348:14:8;4158:30;4726:16;;:34;;;;;4746:14;4726:34;4706:54;;4770:17;4790:11;-1:-1:-1;;;;;4790:16:8;4805:1;4790:16;:50;;;;-1:-1:-1;4818:4:8;4810:25;:30;4790:50;4770:70;;4856:12;4855:13;:30;;;;;4873:12;4872:13;4855:30;4851:91;;;4908:23;;-1:-1:-1;;;4908:23:8;;;;;;;;;;;4851:91;4951:18;;-1:-1:-1;;4951:18:8;4968:1;4951:18;;;4979:67;;;;5013:22;;-1:-1:-1;;;;5013:22:8;-1:-1:-1;;;5013:22:8;;;4979:67;1844:26:14::1;1859:10;1844:14;:26::i;:::-;1881:14;1897:15:::0;1927:16:::1;1916:48;;;;;;;;;;;;:::i;:::-;1880:84;;;;1974:18;1984:7;1974:9;:18::i;:::-;2002;2012:7;2002:9;:18::i;:::-;2030:25;2048:6;2030:17;:25::i;:::-;1834:228;;5070:14:8::0;5066:101;;;5100:23;;-1:-1:-1;;;;5100:23:8;;;5142:14;;-1:-1:-1;21212:50:15;;5142:14:8;;21200:2:15;21185:18;5142:14:8;;;;;;;5066:101;4092:1081;;;;;1760:302:14;:::o;4950:172:0:-;1328:13:14;:11;:13::i;:::-;-1:-1:-1;;;;;5050:22:0;::::1;;::::0;;;:14:::1;:22;::::0;;;;;;5043:29;;-1:-1:-1;;5043:29:0::1;::::0;;5087:28;::::1;::::0;::::1;::::0;5065:6;;5109:5;;;;5087:28:::1;:::i;16854:368::-:0;16932:13;16947:16;16978:8;16967:40;;;;;;;;;;;;:::i;:::-;16931:76;;;;17034:21;17049:5;17034:14;:21::i;:::-;17026:80;;;;-1:-1:-1;;;17026:80:0;;;;;;;:::i;:::-;17124:27;17142:8;17124:17;:27::i;:::-;17116:99;;;;-1:-1:-1;;;17116:99:0;;21475:2:15;17116:99:0;;;21457:21:15;21514:2;21494:18;;;21487:30;21553:34;21533:18;;;21526:62;21624:29;21604:18;;;21597:57;21671:19;;17116:99:0;21273:423:15;8292:118:0;-1:-1:-1;;;;;8375:20:0;8352:4;8375:20;;;:13;:20;;;;;;;;:28;;:20;:28;;8292:118::o;7662:130::-;-1:-1:-1;;;;;7751:26:0;7728:4;7751:26;;;:16;:26;;;;;;;;:34;;:26;:34;;7662:130::o;2332:662:14:-;2414:12;2436:21;2569:51;2591:10;2603:6;2611:8;;2569:21;:51::i;:::-;2738:117;2769:6;2789:1;2804:8;;2738:117;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2738:117:14;-1:-1:-1;2738:17:14;;-1:-1:-1;;2738:117:14:i;:::-;2716:139;;-1:-1:-1;2716:139:14;-1:-1:-1;2716:139:14;2866:122;;2955:8;2949:15;2942:4;2932:8;2928:19;2921:44;20941:311:0;21016:13;21050:8;21039:52;;;;;;;;;;;;:::i;:::-;21015:76;;;;;21110:21;21125:5;21110:14;:21::i;:::-;21102:51;;;;-1:-1:-1;;;21102:51:0;;;;;;;:::i;4551:215::-;1328:13:14;:11;:13::i;:::-;-1:-1:-1;;;;;4670:24:0;::::1;;::::0;;;:16:::1;:24;::::0;;;;;;;-1:-1:-1;;;;;;4670:34:0;::::1;::::0;;;;;;;;;4663:41;;-1:-1:-1;;4663:41:0::1;::::0;;4719:40;::::1;::::0;::::1;::::0;4687:6;;4695:8;;4753:5;;;;4719:40:::1;:::i;12864:333::-:0;12940:31;12986:8;12975:41;;;;;;;;;;;;:::i;:::-;12939:77;;13043:35;13061:6;:16;;;13043:17;:35::i;:::-;13035:111;;;;-1:-1:-1;;;13035:111:0;;23969:2:15;13035:111:0;;;23951:21:15;24008:2;23988:18;;;23981:30;24047:34;24027:18;;;24020:62;24118:33;24098:18;;;24091:61;24169:19;;13035:111:0;23767:427:15;13035:111:0;13178:11;;13156:34;;:21;:34::i;12529:329::-;12604:30;12649:8;12638:40;;;;;;;;;;;;:::i;:::-;12603:75;;12705:35;12723:6;:16;;;12705:17;:35::i;:::-;12697:110;;;;-1:-1:-1;;;12697:110:0;;24783:2:15;12697:110:0;;;24765:21:15;24822:2;24802:18;;;24795:30;24861:34;24841:18;;;24834:62;24932:32;24912:18;;;24905:60;24982:19;;12697:110:0;24581:426:15;9193:269:0;9295:40;;;;;;;;;;;;-1:-1:-1;;;9295:40:0;;;;;9274:69;9288:5;4058:23;9295:40;3899:190;9274:69;9374:39;;;;;;;;;;;;-1:-1:-1;;;9374:39:0;;;;;9353:68;9367:5;4058:23;9374:39;3899:190;21724:339;21812:88;21826:11;21839:53;;;;;;;;;;;;;;;;;;4058:23;;;;;;;;3899:190;21812:88;21910:83;21924:11;21937:48;;;;;;;;;;;;;;;;;;4058:23;;;;;;;;3899:190;20180:603;20318:33;;;;;;;;;;;;-1:-1:-1;;;20318:33:0;;;;;20291:68;20305:11;4058:23;20318:33;3899:190;20291:68;20369:44;20394:11;20407:5;;20369:24;:44::i;:::-;20423;20448:11;20461:5;;20423:24;:44::i;:::-;20722:54;20757:11;20770:5;;20722:34;:54::i;3405:215:7:-;2334:13;:11;:13::i;:::-;-1:-1:-1;;;;;3489:22:7;::::1;3485:91;;3534:31;::::0;-1:-1:-1;;;3534:31:7;;3562:1:::1;3534:31;::::0;::::1;3638:51:15::0;3611:18;;3534:31:7::1;3492:203:15::0;3485:91:7::1;3585:28;3604:8;3585:18;:28::i;:::-;3405:215:::0;:::o;17398:359:0:-;17477:13;17492:16;17523:8;17512:40;;;;;;;;;;;;:::i;:::-;17476:76;;;;17579:21;17594:5;17579:14;:21::i;:::-;17571:70;;;;-1:-1:-1;;;17571:70:0;;25214:2:15;17571:70:0;;;25196:21:15;25253:2;25233:18;;;25226:30;25292:34;25272:18;;;25265:62;-1:-1:-1;;;25343:18:15;;;25336:34;25387:19;;17571:70:0;25012:400:15;7102:166:0;1328:13:14;:11;:13::i;:::-;-1:-1:-1;;;;;7200:20:0;::::1;;::::0;;;:13:::1;:20;::::0;;;;;;7193:27;;-1:-1:-1;;7193:27:0::1;::::0;;7235:26;::::1;::::0;::::1;::::0;7214:5;;7255;;;;7235:26:::1;:::i;4772:172::-:0;1328:13:14;:11;:13::i;:::-;-1:-1:-1;;;;;4864:22:0;::::1;;::::0;;;:14:::1;:22;::::0;;;;;;:29;;-1:-1:-1;;4864:29:0::1;4889:4;4864:29;::::0;;4908;::::1;::::0;::::1;::::0;4879:6;;4931:5;;;;4908:29:::1;:::i;2658:162:7:-:0;966:10:9;2717:7:7;:5;:7::i;:::-;-1:-1:-1;;;;;2717:23:7;;2713:101;;2763:40;;-1:-1:-1;;;2763:40:7;;966:10:9;2763:40:7;;;3638:51:15;3611:18;;2763:40:7;3492:203:15;392:3007:2;514:12;562:7;546:12;562:7;556:2;546:12;:::i;:::-;:23;;538:50;;;;-1:-1:-1;;;538:50:2;;25619:2:15;538:50:2;;;25601:21:15;25658:2;25638:18;;;25631:30;-1:-1:-1;;;25677:18:15;;;25670:44;25731:18;;538:50:2;25417:338:15;538:50:2;626:6;606:16;615:7;626:6;606:16;:::i;:::-;:26;;598:53;;;;-1:-1:-1;;;598:53:2;;25619:2:15;598:53:2;;;25601:21:15;25658:2;25638:18;;;25631:30;-1:-1:-1;;;25677:18:15;;;25670:44;25731:18;;598:53:2;25417:338:15;598:53:2;686:16;695:7;686:6;:16;:::i;:::-;669:6;:13;:33;;661:63;;;;-1:-1:-1;;;661:63:2;;25962:2:15;661:63:2;;;25944:21:15;26001:2;25981:18;;;25974:30;-1:-1:-1;;;26020:18:15;;;26013:47;26077:18;;661:63:2;25760:341:15;661:63:2;735:22;798:15;;830:2099;;;;3082:4;3076:11;3063:24;;3280:1;3269:9;3262:20;3332:4;3321:9;3317:20;3311:4;3304:34;791:2565;;830:2099;1024:4;1018:11;1005:24;;1719:2;1710:7;1706:16;2121:9;2114:17;2108:4;2104:28;2092:9;2081;2077:25;2073:60;2173:7;2169:2;2165:16;2441:6;2427:9;2420:17;2414:4;2410:28;2398:9;2390:6;2386:22;2382:57;2378:70;2203:461;2478:3;2474:2;2471:11;2203:461;;;2632:9;;2621:21;;2523:4;2515:13;;;;2559;2203:461;;;-1:-1:-1;;2686:26:2;;;2906:2;2889:11;-1:-1:-1;;2885:25:2;2879:4;2872:39;-1:-1:-1;791:2565:2;-1:-1:-1;3383:9:2;392:3007;-1:-1:-1;;;;392:3007:2:o;1779:314:3:-;1883:14;;;1983:17;:4;1883:14;1983;:17::i;:::-;1974:26;-1:-1:-1;2016:24:3;:4;304:2;2016:13;:24::i;:::-;2010:30;-1:-1:-1;2059:27:3;507:20;397:1;304:2;507:20;:::i;:::-;2059:4;;:14;:27::i;:::-;2050:36;;1779:314;;;;;:::o;992:138::-;1060:4;507:20;397:1;304:2;507:20;:::i;:::-;304:2;507:20;397:1;304:2;507:20;:::i;:::-;618:23;;;;:::i;:::-;777:24;;;;:::i;:::-;1083:4;:11;:40;;1076:47;;992:138;;;:::o;2635:149::-;2696:12;2727:50;507:20;397:1;304:2;507:20;:::i;:::-;;397:1;304:2;507:20;:::i;:::-;2751:4;:11;:25;;;;:::i;:::-;2727:4;;:50;:10;:50::i;3774:248:7:-;1313:22;3923:8;;-1:-1:-1;;;;;;3941:19:7;;-1:-1:-1;;;;;3941:19:7;;;;;;;;3975:40;;3923:8;;;;;3975:40;;3847:24;;3975:40;3837:185;;3774:248;:::o;3405:416:2:-;3484:7;3526:6;3511:11;3526:6;3520:2;3511:11;:::i;:::-;:21;;3503:52;;;;-1:-1:-1;;;3503:52:2;;26308:2:15;3503:52:2;;;26290:21:15;26347:2;26327:18;;;26320:30;-1:-1:-1;;;26366:18:15;;;26359:48;26424:18;;3503:52:2;26106:342:15;3503:52:2;3590:11;:6;3599:2;3590:11;:::i;:::-;3573:6;:13;:28;;3565:62;;;;-1:-1:-1;;;3565:62:2;;26655:2:15;3565:62:2;;;26637:21:15;26694:2;26674:18;;;26667:30;-1:-1:-1;;;26713:18:15;;;26706:51;26774:18;;3565:62:2;26453:345:15;3565:62:2;-1:-1:-1;3715:30:2;3731:4;3715:30;3709:37;-1:-1:-1;;;3705:71:2;;;3405:416::o;1847:127:7:-;6931:20:8;:18;:20::i;:::-;1929:38:7::1;1954:12;1929:24;:38::i;2429:327:11:-:0;2640:6;;2632:119;;-1:-1:-1;;;2632:119:11;;2574:12;;2588:23;;-1:-1:-1;;;;;2640:6:11;;;;2632:51;;:119;;2693:2;;2705:5;;2720:4;;2734:9;;2632:119;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;2632:119:11;;;;;;;;;;;;:::i;:::-;2619:132;;;;2429:327;;;;;;;:::o;3827:365:2:-;3905:6;3945;3931:10;3945:6;3940:1;3931:10;:::i;:::-;:20;;3923:50;;;;-1:-1:-1;;;3923:50:2;;28306:2:15;3923:50:2;;;28288:21:15;28345:2;28325:18;;;28318:30;-1:-1:-1;;;28364:18:15;;;28357:47;28421:18;;3923:50:2;28104:341:15;3923:50:2;4008:10;:6;4017:1;4008:10;:::i;:::-;3991:6;:13;:27;;3983:60;;;;-1:-1:-1;;;3983:60:2;;28652:2:15;3983:60:2;;;28634:21:15;28691:2;28671:18;;;28664:30;-1:-1:-1;;;28710:18:15;;;28703:50;28770:18;;3983:60:2;28450:344:15;3983:60:2;-1:-1:-1;4120:29:2;4136:3;4120:29;4114:36;;3827:365::o;7084:141:8:-;8870:21;8560:40;-1:-1:-1;;;8560:40:8;;;;7146:73;;7191:17;;-1:-1:-1;;;7191:17:8;;;;;;;;;;;1980:235:7;6931:20:8;:18;:20::i;14:131:15:-;-1:-1:-1;;;;;89:31:15;;79:42;;69:70;;135:1;132;125:12;150:348;202:8;212:6;266:3;259:4;251:6;247:17;243:27;233:55;;284:1;281;274:12;233:55;-1:-1:-1;307:20:15;;-1:-1:-1;;;;;339:30:15;;336:50;;;382:1;379;372:12;336:50;419:4;411:6;407:17;395:29;;471:3;464:4;455:6;447;443:19;439:30;436:39;433:59;;;488:1;485;478:12;433:59;150:348;;;;;:::o;503:546::-;583:6;591;599;652:2;640:9;631:7;627:23;623:32;620:52;;;668:1;665;658:12;620:52;707:9;694:23;726:31;751:5;726:31;:::i;:::-;776:5;-1:-1:-1;832:2:15;817:18;;804:32;-1:-1:-1;;;;;848:30:15;;845:50;;;891:1;888;881:12;845:50;930:59;981:7;972:6;961:9;957:22;930:59;:::i;:::-;503:546;;1008:8;;-1:-1:-1;904:85:15;;-1:-1:-1;;;;503:546:15:o;1054:127::-;1115:10;1110:3;1106:20;1103:1;1096:31;1146:4;1143:1;1136:15;1170:4;1167:1;1160:15;1186:275;1257:2;1251:9;1322:2;1303:13;;-1:-1:-1;;1299:27:15;1287:40;;-1:-1:-1;;;;;1342:34:15;;1378:22;;;1339:62;1336:88;;;1404:18;;:::i;:::-;1440:2;1433:22;1186:275;;-1:-1:-1;1186:275:15:o;1466:186::-;1514:4;-1:-1:-1;;;;;1539:6:15;1536:30;1533:56;;;1569:18;;:::i;:::-;-1:-1:-1;1635:2:15;1614:15;-1:-1:-1;;1610:29:15;1641:4;1606:40;;1466:186::o;1657:695::-;1725:6;1778:2;1766:9;1757:7;1753:23;1749:32;1746:52;;;1794:1;1791;1784:12;1746:52;1834:9;1821:23;-1:-1:-1;;;;;1859:6:15;1856:30;1853:50;;;1899:1;1896;1889:12;1853:50;1922:22;;1975:4;1967:13;;1963:27;-1:-1:-1;1953:55:15;;2004:1;2001;1994:12;1953:55;2044:2;2031:16;2069:52;2085:35;2113:6;2085:35;:::i;:::-;2069:52;:::i;:::-;2144:6;2137:5;2130:21;2192:7;2187:2;2178:6;2174:2;2170:15;2166:24;2163:37;2160:57;;;2213:1;2210;2203:12;2160:57;2268:6;2263:2;2259;2255:11;2250:2;2243:5;2239:14;2226:49;2320:1;2295:18;;;2315:2;2291:27;2284:38;;;;2299:5;1657:695;-1:-1:-1;;;;1657:695:15:o;2357:247::-;2416:6;2469:2;2457:9;2448:7;2444:23;2440:32;2437:52;;;2485:1;2482;2475:12;2437:52;2524:9;2511:23;2543:31;2568:5;2543:31;:::i;:::-;2593:5;2357:247;-1:-1:-1;;;2357:247:15:o;2801:686::-;2889:6;2897;2905;2913;2966:2;2954:9;2945:7;2941:23;2937:32;2934:52;;;2982:1;2979;2972:12;2934:52;3021:9;3008:23;3040:31;3065:5;3040:31;:::i;:::-;3090:5;-1:-1:-1;3147:2:15;3132:18;;3119:32;3160:33;3119:32;3160:33;:::i;:::-;3212:7;-1:-1:-1;3270:2:15;3255:18;;3242:32;-1:-1:-1;;;;;3286:30:15;;3283:50;;;3329:1;3326;3319:12;3283:50;3368:59;3419:7;3410:6;3399:9;3395:22;3368:59;:::i;:::-;2801:686;;;;-1:-1:-1;3446:8:15;-1:-1:-1;;;;2801:686:15:o;3700:173::-;3767:20;;-1:-1:-1;;;;;;3816:32:15;;3806:43;;3796:71;;3863:1;3860;3853:12;3796:71;3700:173;;;:::o;3878:618::-;3966:6;3974;3982;3990;4043:2;4031:9;4022:7;4018:23;4014:32;4011:52;;;4059:1;4056;4049:12;4011:52;4098:9;4085:23;4117:31;4142:5;4117:31;:::i;:::-;4167:5;-1:-1:-1;4191:37:15;4224:2;4209:18;;4191:37;:::i;4501:319::-;4568:6;4576;4629:2;4617:9;4608:7;4604:23;4600:32;4597:52;;;4645:1;4642;4635:12;4597:52;4684:9;4671:23;4703:31;4728:5;4703:31;:::i;:::-;4753:5;-1:-1:-1;4777:37:15;4810:2;4795:18;;4777:37;:::i;:::-;4767:47;;4501:319;;;;;:::o;6438:267::-;6527:6;6522:3;6515:19;6579:6;6572:5;6565:4;6560:3;6556:14;6543:43;-1:-1:-1;6631:1:15;6606:16;;;6624:4;6602:27;;;6595:38;;;;6687:2;6666:15;;;-1:-1:-1;;6662:29:15;6653:39;;;6649:50;;6438:267::o;6710:344::-;-1:-1:-1;;;;;6897:32:15;;6879:51;;6966:2;6961;6946:18;;6939:30;;;-1:-1:-1;;6986:62:15;;7029:18;;7021:6;7013;6986:62;:::i;:::-;6978:70;6710:344;-1:-1:-1;;;;;6710:344:15:o;7059:181::-;7117:4;-1:-1:-1;;;;;7142:6:15;7139:30;7136:56;;;7172:18;;:::i;:::-;-1:-1:-1;7217:1:15;7213:14;7229:4;7209:25;;7059:181::o;7245:250::-;7330:1;7340:113;7354:6;7351:1;7348:13;7340:113;;;7430:11;;;7424:18;7411:11;;;7404:39;7376:2;7369:10;7340:113;;;-1:-1:-1;;7487:1:15;7469:16;;7462:27;7245:250::o;7500:461::-;7553:5;7606:3;7599:4;7591:6;7587:17;7583:27;7573:55;;7624:1;7621;7614:12;7573:55;7657:6;7651:13;7688:52;7704:35;7732:6;7704:35;:::i;7688:52::-;7765:6;7756:7;7749:23;7819:3;7812:4;7803:6;7795;7791:19;7787:30;7784:39;7781:59;;;7836:1;7833;7826:12;7781:59;7849:81;7923:6;7916:4;7907:7;7903:18;7896:4;7888:6;7884:17;7849:81;:::i;:::-;7948:7;7500:461;-1:-1:-1;;;;7500:461:15:o;7966:1050::-;8070:6;8123:2;8111:9;8102:7;8098:23;8094:32;8091:52;;;8139:1;8136;8129:12;8091:52;8172:9;8166:16;-1:-1:-1;;;;;8197:6:15;8194:30;8191:50;;;8237:1;8234;8227:12;8191:50;8260:22;;8313:4;8305:13;;8301:27;-1:-1:-1;8291:55:15;;8342:1;8339;8332:12;8291:55;8375:2;8369:9;8398:62;8414:45;8452:6;8414:45;:::i;8398:62::-;8482:3;8506:6;8501:3;8494:19;8538:2;8533:3;8529:12;8522:19;;8593:2;8583:6;8580:1;8576:14;8572:2;8568:23;8564:32;8550:46;;8619:7;8611:6;8608:19;8605:39;;;8640:1;8637;8630:12;8605:39;8672:2;8668;8664:11;8684:302;8700:6;8695:3;8692:15;8684:302;;;8779:3;8773:10;-1:-1:-1;;;;;8802:11:15;8799:35;8796:55;;;8847:1;8844;8837:12;8796:55;8876:67;8935:7;8930:2;8916:11;8912:2;8908:20;8904:29;8876:67;:::i;:::-;8864:80;;-1:-1:-1;8973:2:15;8964:12;;;;8717;8684:302;;;-1:-1:-1;9005:5:15;7966:1050;-1:-1:-1;;;;;;7966:1050:15:o;9021:127::-;9082:10;9077:3;9073:20;9070:1;9063:31;9113:4;9110:1;9103:15;9137:4;9134:1;9127:15;9153:376;9270:12;;9318:4;9307:16;;9301:23;-1:-1:-1;;;;;;9342:29:15;;;9270:12;9394:1;9383:13;;9380:143;;;-1:-1:-1;;;;;;9455:1:15;9451:14;;;9448:1;9444:22;9440:49;;;9432:58;;9428:85;;-1:-1:-1;9380:143:15;;;9153:376;;;:::o;9534:127::-;9595:10;9590:3;9586:20;9583:1;9576:31;9626:4;9623:1;9616:15;9650:4;9647:1;9640:15;9666:128;9733:9;;;9754:11;;;9751:37;;;9768:18;;:::i;10219:146::-;10306:13;;10328:31;10306:13;10328:31;:::i;10370:372::-;10457:6;10465;10518:2;10506:9;10497:7;10493:23;10489:32;10486:52;;;10534:1;10531;10524:12;10486:52;10566:9;10560:16;10585:31;10610:5;10585:31;:::i;:::-;10706:2;10691:18;;;;10685:25;10635:5;;10685:25;;-1:-1:-1;;;10370:372:15:o;11176:404::-;11378:2;11360:21;;;11417:2;11397:18;;;11390:30;11456:34;11451:2;11436:18;;11429:62;-1:-1:-1;;;11522:2:15;11507:18;;11500:38;11570:3;11555:19;;11176:404::o;11946:331::-;12051:9;12062;12104:8;12092:10;12089:24;12086:44;;;12126:1;12123;12116:12;12086:44;12155:6;12145:8;12142:20;12139:40;;;12175:1;12172;12165:12;12139:40;-1:-1:-1;;12201:23:15;;;12246:25;;;;;-1:-1:-1;11946:331:15:o;12282:338::-;12402:19;;-1:-1:-1;;;;;;12439:29:15;;;12488:1;12480:10;;12477:137;;;-1:-1:-1;;;;;;12549:1:15;12545:11;;;12542:1;12538:19;12534:46;;;12526:55;;12522:82;;-1:-1:-1;12477:137:15;;12282:338;;;;:::o;13383:442::-;13471:6;13479;13532:2;13520:9;13511:7;13507:23;13503:32;13500:52;;;13548:1;13545;13538:12;13500:52;13593:16;;13677:2;13662:18;;13656:25;13593:16;;-1:-1:-1;;;;;;13693:30:15;;13690:50;;;13736:1;13733;13726:12;13690:50;13759:60;13811:7;13802:6;13791:9;13787:22;13759:60;:::i;:::-;13749:70;;;13383:442;;;;;:::o;14659:135::-;14698:3;14719:17;;;14716:43;;14739:18;;:::i;:::-;-1:-1:-1;14786:1:15;14775:13;;14659:135::o;14799:440::-;-1:-1:-1;;;;;15012:32:15;;14994:51;;-1:-1:-1;;;;;;15081:33:15;;15076:2;15061:18;;15054:61;15151:2;15146;15131:18;;15124:30;;;-1:-1:-1;;15171:62:15;;15214:18;;15206:6;15198;15171:62;:::i;:::-;15163:70;14799:440;-1:-1:-1;;;;;;14799:440:15:o;15244:514::-;15348:6;15356;15364;15417:2;15405:9;15396:7;15392:23;15388:32;15385:52;;;15433:1;15430;15423:12;15385:52;15465:9;15459:16;15484:31;15509:5;15484:31;:::i;:::-;15605:2;15590:18;;15584:25;15680:2;15665:18;;15659:25;15534:5;;-1:-1:-1;15584:25:15;-1:-1:-1;15693:33:15;15659:25;15693:33;:::i;:::-;15745:7;15735:17;;;15244:514;;;;;:::o;15763:341::-;15965:2;15947:21;;;16004:2;15984:18;;;15977:30;-1:-1:-1;;;16038:2:15;16023:18;;16016:47;16095:2;16080:18;;15763:341::o;16519:555::-;16616:6;16624;16632;16685:2;16673:9;16664:7;16660:23;16656:32;16653:52;;;16701:1;16698;16691:12;16653:52;16746:16;;16852:2;16837:18;;16831:25;16926:2;16911:18;;16905:25;16746:16;;-1:-1:-1;16831:25:15;-1:-1:-1;;;;;;16942:30:15;;16939:50;;;16985:1;16982;16975:12;16939:50;17008:60;17060:7;17051:6;17040:9;17036:22;17008:60;:::i;:::-;16998:70;;;16519:555;;;;;:::o;17079:1391::-;17218:6;17226;17234;17242;17250;17303:3;17291:9;17282:7;17278:23;17274:33;17271:53;;;17320:1;17317;17310:12;17271:53;17365:16;;17471:2;17456:18;;17450:25;17545:2;17530:18;;17524:25;17365:16;;-1:-1:-1;17450:25:15;-1:-1:-1;;;;;;17561:30:15;;17558:50;;;17604:1;17601;17594:12;17558:50;17627:22;;17680:4;17672:13;;17668:27;-1:-1:-1;17658:55:15;;17709:1;17706;17699:12;17658:55;17742:2;17736:9;17765:62;17781:45;17819:6;17781:45;:::i;17765:62::-;17849:3;17873:6;17868:3;17861:19;17905:2;17900:3;17896:12;17889:19;;17960:2;17950:6;17947:1;17943:14;17939:2;17935:23;17931:32;17917:46;;17986:7;17978:6;17975:19;17972:39;;;18007:1;18004;17997:12;17972:39;18039:2;18035;18031:11;18020:22;;18051:216;18067:6;18062:3;18059:15;18051:216;;;18142:3;18136:10;18159:33;18184:7;18159:33;:::i;:::-;18205:20;;18254:2;18084:12;;;;18245;;;;18051:216;;;18286:5;-1:-1:-1;18310:57:15;;-1:-1:-1;;;18363:2:15;18348:18;;18310:57;:::i;:::-;18433:3;18418:19;;;;18412:26;17079:1391;;;;-1:-1:-1;17079:1391:15;;18300:67;18412:26;17079:1391;-1:-1:-1;;17079:1391:15:o;18960:259::-;19038:6;19091:2;19079:9;19070:7;19066:23;19062:32;19059:52;;;19107:1;19104;19097:12;19059:52;19139:9;19133:16;19158:31;19183:5;19158:31;:::i;19224:410::-;19426:2;19408:21;;;19465:2;19445:18;;;19438:30;19504:34;19499:2;19484:18;;19477:62;-1:-1:-1;;;19570:2:15;19555:18;;19548:44;19624:3;19609:19;;19224:410::o;20117:125::-;20182:9;;;20203:10;;;20200:36;;;20216:18;;:::i;20247:401::-;20449:2;20431:21;;;20488:2;20468:18;;;20461:30;20527:34;20522:2;20507:18;;20500:62;-1:-1:-1;;;20593:2:15;20578:18;;20571:35;20638:3;20623:19;;20247:401::o;20653:::-;20748:6;20756;20809:2;20797:9;20788:7;20784:23;20780:32;20777:52;;;20825:1;20822;20815:12;20777:52;20857:9;20851:16;20876:31;20901:5;20876:31;:::i;:::-;20976:2;20961:18;;20955:25;20926:5;;-1:-1:-1;20989:33:15;20955:25;20989:33;:::i;:::-;21041:7;21031:17;;;20653:401;;;;;:::o;21701:628::-;21814:6;21822;21830;21838;21891:3;21879:9;21870:7;21866:23;21862:33;21859:53;;;21908:1;21905;21898:12;21859:53;21940:9;21934:16;21959:31;21984:5;21959:31;:::i;:::-;22080:2;22065:18;;22059:25;22155:2;22140:18;;22134:25;22009:5;;-1:-1:-1;22059:25:15;-1:-1:-1;22168:33:15;22134:25;22168:33;:::i;:::-;22293:2;22278:18;;;;22272:25;21701:628;;;;-1:-1:-1;;;21701:628:15:o;22334:1045::-;22409:5;22457:4;22445:9;22440:3;22436:19;22432:30;22429:50;;;22475:1;22472;22465:12;22429:50;22528:2;22522:9;22570:4;22558:17;;-1:-1:-1;;;;;22590:34:15;;22626:22;;;22587:62;22584:88;;;22652:18;;:::i;:::-;22692:10;22688:2;22681:22;;22721:6;22712:15;;22756:9;22750:16;-1:-1:-1;;;;;22781:6:15;22778:30;22775:50;;;22821:1;22818;22811:12;22775:50;22849:56;22901:3;22892:6;22881:9;22877:22;22849:56;:::i;:::-;22841:6;22834:72;;22951:2;22940:9;22936:18;22930:25;22964:33;22989:7;22964:33;:::i;:::-;23025:2;23013:15;;23006:32;23104:2;23089:18;;;23083:25;23124:15;;;23117:32;23215:2;23200:18;;;23194:25;23235:15;;;23228:32;23326:3;23311:19;;;23305:26;23347:16;;23340:33;;;;22334:1045;;-1:-1:-1;22334:1045:15:o;23384:378::-;23487:6;23540:2;23528:9;23519:7;23515:23;23511:32;23508:52;;;23556:1;23553;23546:12;23508:52;23589:9;23583:16;-1:-1:-1;;;;;23614:6:15;23611:30;23608:50;;;23654:1;23651;23644:12;23608:50;23677:79;23748:7;23739:6;23728:9;23724:22;23677:79;:::i;26803:802::-;27075:1;27071;27066:3;27062:11;27058:19;27050:6;27046:32;27035:9;27028:51;27115:6;27110:2;27099:9;27095:18;27088:34;27158:3;27153:2;27142:9;27138:18;27131:31;27009:4;27191:6;27185:13;27235:6;27229:3;27218:9;27214:19;27207:35;27251:80;27324:6;27318:3;27307:9;27303:19;27298:2;27290:6;27286:15;27251:80;:::i;:::-;27392:2;27371:15;-1:-1:-1;;27367:29:15;27352:45;;27399:3;27348:55;;-1:-1:-1;27433:1:15;27422:13;;27412:144;;27478:10;27473:3;27469:20;27466:1;27459:31;27513:4;27510:1;27503:15;27541:4;27538:1;27531:15;27412:144;27592:6;27587:2;27576:9;27572:18;27565:34;26803:802;;;;;;;:::o;27610:489::-;27695:6;27703;27756:2;27744:9;27735:7;27731:23;27727:32;27724:52;;;27772:1;27769;27762:12;27724:52;27804:9;27798:16;27857:5;27850:13;27843:21;27836:5;27833:32;27823:60;;27879:1;27876;27869:12;27823:60;27951:2;27936:18;;27930:25;27902:5;;-1:-1:-1;;;;;;27967:30:15;;27964:50;;;28010:1;28007;28000:12","linkReferences":{}},"methodIdentifiers":{"_validateCallInternal(address,address,bytes)":"2674111e","allowApprovalDestination(address,string)":"98b3cc39","allowAsset(address,string)":"86b6dbe5","allowCallSite(address,bytes4,string)":"5e4ccace","allowDelegationApprovalDestination(address,string)":"63cc17f2","allowReceiver(address,string)":"72e548a9","allowSender(address,string)":"fa2c59c8","allowWithdrawDestination(address,string)":"5ace1d92","allowedApprovalDestinations(address)":"eb0de042","allowedAssets(address)":"a4c1cccb","allowedCallSites(address,bytes4)":"a847cf4d","allowedDelegationApprovalDestinations(address)":"4b941268","allowedReceivers(address)":"3cf20025","allowedSenders(address)":"fadbcf48","allowedWithdrawDestinations(address)":"07ef00cf","avatar()":"5aef7de6","callSiteCount()":"a9fc3d4f","getGovernanceAddress()":"73252494","getInternalVersion()":"fdedfa27","isAllowedApprovalDestination(address)":"1d49039c","isAllowedAsset(address)":"c537bed0","isAllowedCallSite(address,bytes4)":"713ebf3b","isAllowedDelegationApprovalDestination(address)":"2dc32a61","isAllowedReceiver(address)":"d075f9bb","isAllowedSender(address)":"be8c97b0","isAllowedWithdrawDestination(address)":"efb47bff","owner()":"8da5cb5b","performCall(address,bytes)":"d2f73e3d","removeApprovalDestination(address,string)":"04a3ba25","removeAsset(address,string)":"f901dc33","removeCallSite(address,bytes4,string)":"d7334c9d","removeDelegationApprovalDestination(address,string)":"f259a073","removeReceiver(address,string)":"2d12d788","removeSender(address,string)":"a67e1f54","removeWithdrawDestination(address,string)":"8c2fdf9e","renounceOwnership()":"715018a6","setAvatar(address)":"086cfca8","setTarget(address)":"776d1a01","setUp(bytes)":"a4f9edbf","target()":"d4b83992","transferOwnership(address)":"f2fde38b","validate1deltaPath(bytes)":"a1b6b430","validateCall(address,address,bytes)":"6d5025f1","validateUniswapV3Path(bytes)":"1c123e77","validate_1deltaDeposit(bytes)":"c4cc9dba","validate_1deltaMulticall(bytes)":"07ac3562","validate_1deltaWithdraw(bytes)":"f76081e4","validate_aaveSupply(bytes)":"d4c0fe45","validate_aaveWithdraw(bytes)":"6a6d5cc8","validate_approve(bytes)":"4b956bd8","validate_approveDelegation(bytes)":"957dc6c0","validate_exactInput(bytes)":"ebe26816","validate_exactOutput(bytes)":"e98539c5","validate_flashSwapAllOut(bytes)":"425f49d0","validate_flashSwapExactInt(bytes)":"ca963c50","validate_flashSwapExactOut(bytes)":"6ad11353","validate_swapExactTokensForTokens(bytes)":"761bcd9c","validate_transfer(bytes)":"1710a4f2","validate_transferERC20AllIn(bytes)":"7ea44655","validate_transferERC20In(bytes)":"59b92be9","whitelistAaveV3(address,string)":"f20e85e0","whitelistOnedelta(address,address,string)":"f26749f3","whitelistToken(address,string)":"ee5462cc","whitelistTokenForDelegation(address,string)":"6d9a1423","whitelistUniswapV2Router(address,string)":"3ea35551","whitelistUniswapV3Router(address,string)":"34ec8d7e"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.26+commit.8a97fa7a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_target\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidInitialization\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInitializing\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"OwnableInvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"OwnableUnauthorizedAccount\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"ApprovalDestinationApproved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"ApprovalDestinationRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"AssetApproved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"AssetRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousAvatar\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAvatar\",\"type\":\"address\"}],\"name\":\"AvatarSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes4\",\"name\":\"selector\",\"type\":\"bytes4\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"CallSiteApproved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes4\",\"name\":\"selector\",\"type\":\"bytes4\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"CallSiteRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"DelegationApprovalDestinationApproved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"DelegationApprovalDestinationRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"version\",\"type\":\"uint64\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"ReceiverApproved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"ReceiverRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"SenderApproved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"SenderRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousTarget\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newTarget\",\"type\":\"address\"}],\"name\":\"TargetSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"WithdrawDestinationApproved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"WithdrawDestinationRemoved\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callDataWithSelector\",\"type\":\"bytes\"}],\"name\":\"_validateCallInternal\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"allowApprovalDestination\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"allowAsset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"selector\",\"type\":\"bytes4\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"allowCallSite\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"allowDelegationApprovalDestination\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"allowReceiver\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"allowSender\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"allowWithdrawDestination\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"allowedApprovalDestinations\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"allowedAssets\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"selector\",\"type\":\"bytes4\"}],\"name\":\"allowedCallSites\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"allowedDelegationApprovalDestinations\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"allowedReceivers\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"allowedSenders\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"allowedWithdrawDestinations\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"avatar\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"callSiteCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getGovernanceAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getInternalVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"isAllowedApprovalDestination\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isAllowedAsset\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"selector\",\"type\":\"bytes4\"}],\"name\":\"isAllowedCallSite\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"isAllowedDelegationApprovalDestination\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"isAllowedReceiver\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"isAllowedSender\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"isAllowedWithdrawDestination\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"performCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"removeApprovalDestination\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"removeAsset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"selector\",\"type\":\"bytes4\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"removeCallSite\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"removeDelegationApprovalDestination\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"removeReceiver\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"removeSender\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"removeWithdrawDestination\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_avatar\",\"type\":\"address\"}],\"name\":\"setAvatar\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_target\",\"type\":\"address\"}],\"name\":\"setTarget\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"initializeParams\",\"type\":\"bytes\"}],\"name\":\"setUp\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"target\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"path\",\"type\":\"bytes\"}],\"name\":\"validate1deltaPath\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callDataWithSelector\",\"type\":\"bytes\"}],\"name\":\"validateCall\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"path\",\"type\":\"bytes\"}],\"name\":\"validateUniswapV3Path\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_1deltaDeposit\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_1deltaMulticall\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_1deltaWithdraw\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_aaveSupply\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_aaveWithdraw\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_approve\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_approveDelegation\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_exactInput\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_exactOutput\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_flashSwapAllOut\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_flashSwapExactInt\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_flashSwapExactOut\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_swapExactTokensForTokens\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_transfer\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_transferERC20AllIn\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"validate_transferERC20In\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"lendingPool\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"whitelistAaveV3\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"brokerProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"lendingPool\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"whitelistOnedelta\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"whitelistToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"whitelistTokenForDelegation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"whitelistUniswapV2Router\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"notes\",\"type\":\"string\"}],\"name\":\"whitelistUniswapV3Router\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"errors\":{\"InvalidInitialization()\":[{\"details\":\"The contract is already initialized.\"}],\"NotInitializing()\":[{\"details\":\"The contract is not initializing.\"}],\"OwnableInvalidOwner(address)\":[{\"details\":\"The owner is not a valid owner account. (eg. `address(0)`)\"}],\"OwnableUnauthorizedAccount(address)\":[{\"details\":\"The caller account is not authorized to perform an operation.\"}]},\"events\":{\"AvatarSet(address,address)\":{\"details\":\"Emitted each time the avatar is set.\"},\"Initialized(uint64)\":{\"details\":\"Triggered when the contract has been initialized or reinitialized.\"},\"TargetSet(address,address)\":{\"details\":\"Emitted each time the Target is set.\"}},\"kind\":\"dev\",\"methods\":{\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner.\"},\"setAvatar(address)\":{\"details\":\"Sets the avatar to a new avatar (`newAvatar`).\"},\"setTarget(address)\":{\"details\":\"Sets the target to a new target (`newTarget`).\"},\"setUp(bytes)\":{\"details\":\"Initialize function, will be triggered when a new proxy is deployed\",\"params\":{\"initializeParams\":\"Parameters of initialization encoded https://gist.github.com/auryn-macmillan/841906d0bc6c2624e83598cdfac17de8\"}},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"getGovernanceAddress()\":{\"notice\":\"Get the address of the proto DAO\"},\"getInternalVersion()\":{\"notice\":\"Track version during internal development. We bump up when new whitelistings added.\"},\"performCall(address,bytes)\":{\"notice\":\"The main entry point for the trade executor. - Checks for the whitelisted sender (=trade executor hot wallet) - Check for the allowed callsites/token whitelists/etc. - Execute transaction on behalf of Safe\"},\"setAvatar(address)\":{\"notice\":\"Can only be called by the current owner.\"},\"setTarget(address)\":{\"notice\":\"Can only be called by the current owner.\"},\"validate1deltaPath(bytes)\":{\"notice\":\"Our implementation of 1delta path decoding and validation using similar approach as Uniswap v3 `Path.sol` Read more: - How 1delta encodes the path: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/test-ts/1delta/shared/aggregatorPath.ts#L5-L32 - How 1delta decodes the path: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/MarginTrading.sol#L54-L60\"},\"validateCall(address,address,bytes)\":{\"notice\":\"Revert if the smart contract call is not allowed\"}},\"notice\":\"Trading Strategy integration as Zodiac Module. - Add automated trading strategy support w/whitelisted trading universe and and trade executors - Support Lagoon, Gnosis Safe and other Gnosis Safe-based ecosystems which support Zodiac modules - Owner should point to Gnosis Safe / DAO This is initial, MVP, version. Notes - See VelvetSafeModule as an example https://github.com/Velvet-Capital/velvet-core/blob/9d487937d0569c12e85b436a1c6f3e68a1dc8c44/contracts/vault/VelvetSafeModule.sol#L16\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/TradingStrategyModuleV0.sol\":\"TradingStrategyModuleV0\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@gnosis.pm/=lib/\",\":@guard/=../guard/src/\",\":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/\",\":@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/src/\",\":halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/\",\":safe-contracts/=lib/safe-contracts/contracts/\",\":zodiac/=lib/zodiac/contracts/\"]},\"sources\":{\"../guard/src/GuardV0Base.sol\":{\"keccak256\":\"0x33104cff571c90635cd59d2566b4e4d4bd4afdf1c8c7e10253aed898dd63f2e6\",\"urls\":[\"bzz-raw://0eeca7f95edb6e3d6e20db92d39aa0a19170d661619d9aa24fb05bf19d86a897\",\"dweb:/ipfs/QmdULXJcgqLEgXgXRwKbuvonGq3vRPv9Bnh61p4ZpFt4JJ\"]},\"../guard/src/IGuard.sol\":{\"keccak256\":\"0xcb7ae06b3f3406e29c4012a1ba0ff81a8d30e6e760c15241e9182069c34db1c4\",\"urls\":[\"bzz-raw://2ef7e5cc3383a1439396e6c9ff0821b60e461c502d90d83b81b104ad88362f6a\",\"dweb:/ipfs/QmXQWP9shBmdD219btryxgfhY3RExjASgofb41SpAGdWhW\"]},\"../guard/src/lib/BytesLib.sol\":{\"keccak256\":\"0x7446c0f0de6eb147f0f3e40b3a72e39d02a951e8c453437dcc6b3c06007bc62a\",\"license\":\"Unlicense\",\"urls\":[\"bzz-raw://1b2b64668ea4287dfe75e1029fef387b0e3ae191b784d1bc5be01edac6d1d5c5\",\"dweb:/ipfs/Qma3zAe3Svez1kdGc5F356m9tRjCDg8F8tAocbd5Qk13PE\"]},\"../guard/src/lib/Path.sol\":{\"keccak256\":\"0xb22c562b5175d50dbcc2224325666090d985f052abdcfe275c8dfc884e34de61\",\"license\":\"GPL-2.0-or-later\",\"urls\":[\"bzz-raw://27aeff27314476ab3c77313ae1f0c5b2442b86315f5ce741cb2150a87c3cbad3\",\"dweb:/ipfs/QmQNno2g1CJvjnH6ych4LuHr1UZDEZPACgYG4XvivxGrqV\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol\":{\"keccak256\":\"0xc163fcf9bb10138631a9ba5564df1fa25db9adff73bd9ee868a8ae1858fe093a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://9706d43a0124053d9880f6e31a59f31bc0a6a3dc1acd66ce0a16e1111658c5f6\",\"dweb:/ipfs/QmUFmfowzkRwGtDu36cXV9SPTBHJ3n7dG9xQiK5B28jTf2\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol\":{\"keccak256\":\"0x631188737069917d2f909d29ce62c4d48611d326686ba6683e26b72a23bfac0b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7a61054ae84cd6c4d04c0c4450ba1d6de41e27e0a2c4f1bcdf58f796b401c609\",\"dweb:/ipfs/QmUvtdp7X1mRVyC3CsHrtPbgoqWaXHp3S1ZR24tpAQYJWM\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol\":{\"keccak256\":\"0xdbef5f0c787055227243a7318ef74c8a5a1108ca3a07f2b3a00ef67769e1e397\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://08e39f23d5b4692f9a40803e53a8156b72b4c1f9902a88cd65ba964db103dab9\",\"dweb:/ipfs/QmPKn6EYDgpga7KtpkA8wV2yJCYGMtc9K4LkJfhKX2RVSV\"]},\"lib/safe-contracts/contracts/common/Enum.sol\":{\"keccak256\":\"0x473e45b1a5cc47be494b0e123c9127f0c11c1e0992a321ae5a644c0bfdb2c14f\",\"license\":\"LGPL-3.0-only\",\"urls\":[\"bzz-raw://948e6d5a8bd7377f7795b6e28584eab9be5c0a8db240bf5e606744a175238c3d\",\"dweb:/ipfs/QmQQaUmQfpuejV41jbxKSTgEBYYCcVTjCsDMgf2htgz6ej\"]},\"lib/zodiac/contracts/core/Module.sol\":{\"keccak256\":\"0xdf5df30dee53582cbdfce3aa4a860ed3326062a5806b82fd99c21854b329bda4\",\"license\":\"LGPL-3.0-only\",\"urls\":[\"bzz-raw://7e25ef9cbfb973a3ae8237f8ea06772378bba1a2bc0c4f3b913f2a54dbb12525\",\"dweb:/ipfs/Qmcc3ozbfjAkQtbXqq2DSeL3nJs4rNqC9LEpdkWJ4tFcNS\"]},\"lib/zodiac/contracts/factory/FactoryFriendly.sol\":{\"keccak256\":\"0xdc17580aaefd994be56dced6c9765695807eeb996b05cbd1516171ca0784d83d\",\"license\":\"LGPL-3.0-only\",\"urls\":[\"bzz-raw://58402770f94d469903388ef93aca179f53638d64c76d961b8d86d3042212f0fd\",\"dweb:/ipfs/QmZFunrkQsX3765bpbRporGc7ZBysKdR3yryvzPuEKszNY\"]},\"lib/zodiac/contracts/interfaces/IAvatar.sol\":{\"keccak256\":\"0xb7dfa794e2ceec98635b3c0e6bb810e6001db4619c5f024413bdb9575efea6bf\",\"license\":\"LGPL-3.0-only\",\"urls\":[\"bzz-raw://5900e65d1580f648b16f27d913fa000868af1919b5326155e2fd13a73691c893\",\"dweb:/ipfs/QmUujxEr28S93i3bVYvhhuXCAb8P44ra8UCzPXcQXZbbka\"]},\"src/TradingStrategyModuleV0.sol\":{\"keccak256\":\"0xa7f049be9d7c61aa82467f69cd213cf517942f15b73d2bf11acdd7d6694eefb6\",\"urls\":[\"bzz-raw://4400085916d42de23c1eb576a95e62d573f010a072d2774f396463d756d2c868\",\"dweb:/ipfs/QmNuMqWVbaAC6dvjumq5W7ypcDUZ1t9kVfd6z4RptBcjB8\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.26+commit.8a97fa7a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_target","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"type":"error","name":"InvalidInitialization"},{"inputs":[],"type":"error","name":"NotInitializing"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"type":"error","name":"OwnableInvalidOwner"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"type":"error","name":"OwnableUnauthorizedAccount"},{"inputs":[{"internalType":"address","name":"sender","type":"address","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"ApprovalDestinationApproved","anonymous":false},{"inputs":[{"internalType":"address","name":"sender","type":"address","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"ApprovalDestinationRemoved","anonymous":false},{"inputs":[{"internalType":"address","name":"sender","type":"address","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"AssetApproved","anonymous":false},{"inputs":[{"internalType":"address","name":"sender","type":"address","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"AssetRemoved","anonymous":false},{"inputs":[{"internalType":"address","name":"previousAvatar","type":"address","indexed":true},{"internalType":"address","name":"newAvatar","type":"address","indexed":true}],"type":"event","name":"AvatarSet","anonymous":false},{"inputs":[{"internalType":"address","name":"target","type":"address","indexed":false},{"internalType":"bytes4","name":"selector","type":"bytes4","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"CallSiteApproved","anonymous":false},{"inputs":[{"internalType":"address","name":"target","type":"address","indexed":false},{"internalType":"bytes4","name":"selector","type":"bytes4","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"CallSiteRemoved","anonymous":false},{"inputs":[{"internalType":"address","name":"sender","type":"address","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"DelegationApprovalDestinationApproved","anonymous":false},{"inputs":[{"internalType":"address","name":"sender","type":"address","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"DelegationApprovalDestinationRemoved","anonymous":false},{"inputs":[{"internalType":"uint64","name":"version","type":"uint64","indexed":false}],"type":"event","name":"Initialized","anonymous":false},{"inputs":[{"internalType":"address","name":"previousOwner","type":"address","indexed":true},{"internalType":"address","name":"newOwner","type":"address","indexed":true}],"type":"event","name":"OwnershipTransferred","anonymous":false},{"inputs":[{"internalType":"address","name":"sender","type":"address","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"ReceiverApproved","anonymous":false},{"inputs":[{"internalType":"address","name":"sender","type":"address","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"ReceiverRemoved","anonymous":false},{"inputs":[{"internalType":"address","name":"sender","type":"address","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"SenderApproved","anonymous":false},{"inputs":[{"internalType":"address","name":"sender","type":"address","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"SenderRemoved","anonymous":false},{"inputs":[{"internalType":"address","name":"previousTarget","type":"address","indexed":true},{"internalType":"address","name":"newTarget","type":"address","indexed":true}],"type":"event","name":"TargetSet","anonymous":false},{"inputs":[{"internalType":"address","name":"sender","type":"address","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"WithdrawDestinationApproved","anonymous":false},{"inputs":[{"internalType":"address","name":"sender","type":"address","indexed":false},{"internalType":"string","name":"notes","type":"string","indexed":false}],"type":"event","name":"WithdrawDestinationRemoved","anonymous":false},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callDataWithSelector","type":"bytes"}],"stateMutability":"view","type":"function","name":"_validateCallInternal"},{"inputs":[{"internalType":"address","name":"destination","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"allowApprovalDestination"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"allowAsset"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"allowCallSite"},{"inputs":[{"internalType":"address","name":"destination","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"allowDelegationApprovalDestination"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"allowReceiver"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"allowSender"},{"inputs":[{"internalType":"address","name":"destination","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"allowWithdrawDestination"},{"inputs":[{"internalType":"address","name":"destination","type":"address"}],"stateMutability":"view","type":"function","name":"allowedApprovalDestinations","outputs":[{"internalType":"bool","name":"allowed","type":"bool"}]},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"stateMutability":"view","type":"function","name":"allowedAssets","outputs":[{"internalType":"bool","name":"allowed","type":"bool"}]},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes4","name":"selector","type":"bytes4"}],"stateMutability":"view","type":"function","name":"allowedCallSites","outputs":[{"internalType":"bool","name":"allowed","type":"bool"}]},{"inputs":[{"internalType":"address","name":"destination","type":"address"}],"stateMutability":"view","type":"function","name":"allowedDelegationApprovalDestinations","outputs":[{"internalType":"bool","name":"allowed","type":"bool"}]},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"stateMutability":"view","type":"function","name":"allowedReceivers","outputs":[{"internalType":"bool","name":"allowed","type":"bool"}]},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"stateMutability":"view","type":"function","name":"allowedSenders","outputs":[{"internalType":"bool","name":"allowed","type":"bool"}]},{"inputs":[{"internalType":"address","name":"destination","type":"address"}],"stateMutability":"view","type":"function","name":"allowedWithdrawDestinations","outputs":[{"internalType":"bool","name":"allowed","type":"bool"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"avatar","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"callSiteCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getGovernanceAddress","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[],"stateMutability":"pure","type":"function","name":"getInternalVersion","outputs":[{"internalType":"uint8","name":"","type":"uint8"}]},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"stateMutability":"view","type":"function","name":"isAllowedApprovalDestination","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"stateMutability":"view","type":"function","name":"isAllowedAsset","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes4","name":"selector","type":"bytes4"}],"stateMutability":"view","type":"function","name":"isAllowedCallSite","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"stateMutability":"view","type":"function","name":"isAllowedDelegationApprovalDestination","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"stateMutability":"view","type":"function","name":"isAllowedReceiver","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"stateMutability":"view","type":"function","name":"isAllowedSender","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"stateMutability":"view","type":"function","name":"isAllowedWithdrawDestination","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"performCall"},{"inputs":[{"internalType":"address","name":"destination","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"removeApprovalDestination"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"removeAsset"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"removeCallSite"},{"inputs":[{"internalType":"address","name":"destination","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"removeDelegationApprovalDestination"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"removeReceiver"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"removeSender"},{"inputs":[{"internalType":"address","name":"destination","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"removeWithdrawDestination"},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"renounceOwnership"},{"inputs":[{"internalType":"address","name":"_avatar","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"setAvatar"},{"inputs":[{"internalType":"address","name":"_target","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"setTarget"},{"inputs":[{"internalType":"bytes","name":"initializeParams","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"setUp"},{"inputs":[],"stateMutability":"view","type":"function","name":"target","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"transferOwnership"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate1deltaPath"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callDataWithSelector","type":"bytes"}],"stateMutability":"view","type":"function","name":"validateCall"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"}],"stateMutability":"view","type":"function","name":"validateUniswapV3Path"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_1deltaDeposit"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_1deltaMulticall"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_1deltaWithdraw"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_aaveSupply"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_aaveWithdraw"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_approve"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_approveDelegation"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_exactInput"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_exactOutput"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_flashSwapAllOut"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_flashSwapExactInt"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_flashSwapExactOut"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_swapExactTokensForTokens"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_transfer"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_transferERC20AllIn"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"}],"stateMutability":"view","type":"function","name":"validate_transferERC20In"},{"inputs":[{"internalType":"address","name":"lendingPool","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"whitelistAaveV3"},{"inputs":[{"internalType":"address","name":"brokerProxy","type":"address"},{"internalType":"address","name":"lendingPool","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"whitelistOnedelta"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"whitelistToken"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"whitelistTokenForDelegation"},{"inputs":[{"internalType":"address","name":"router","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"whitelistUniswapV2Router"},{"inputs":[{"internalType":"address","name":"router","type":"address"},{"internalType":"string","name":"notes","type":"string"}],"stateMutability":"nonpayable","type":"function","name":"whitelistUniswapV3Router"}],"devdoc":{"kind":"dev","methods":{"owner()":{"details":"Returns the address of the current owner."},"renounceOwnership()":{"details":"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner."},"setAvatar(address)":{"details":"Sets the avatar to a new avatar (`newAvatar`)."},"setTarget(address)":{"details":"Sets the target to a new target (`newTarget`)."},"setUp(bytes)":{"details":"Initialize function, will be triggered when a new proxy is deployed","params":{"initializeParams":"Parameters of initialization encoded https://gist.github.com/auryn-macmillan/841906d0bc6c2624e83598cdfac17de8"}},"transferOwnership(address)":{"details":"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner."}},"version":1},"userdoc":{"kind":"user","methods":{"getGovernanceAddress()":{"notice":"Get the address of the proto DAO"},"getInternalVersion()":{"notice":"Track version during internal development. We bump up when new whitelistings added."},"performCall(address,bytes)":{"notice":"The main entry point for the trade executor. - Checks for the whitelisted sender (=trade executor hot wallet) - Check for the allowed callsites/token whitelists/etc. - Execute transaction on behalf of Safe"},"setAvatar(address)":{"notice":"Can only be called by the current owner."},"setTarget(address)":{"notice":"Can only be called by the current owner."},"validate1deltaPath(bytes)":{"notice":"Our implementation of 1delta path decoding and validation using similar approach as Uniswap v3 `Path.sol` Read more: - How 1delta encodes the path: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/test-ts/1delta/shared/aggregatorPath.ts#L5-L32 - How 1delta decodes the path: https://github.com/1delta-DAO/contracts-delegation/blob/4f27e1593c564c419ff042cdd932ed52d04216bf/contracts/1delta/modules/aave/MarginTrading.sol#L54-L60"},"validateCall(address,address,bytes)":{"notice":"Revert if the smart contract call is not allowed"}},"version":1}},"settings":{"remappings":["@gnosis.pm/=lib/","@guard/=../guard/src/","@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/","@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/","erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/","forge-std/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/","safe-contracts/=lib/safe-contracts/contracts/","zodiac/=lib/zodiac/contracts/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/TradingStrategyModuleV0.sol":"TradingStrategyModuleV0"},"evmVersion":"paris","libraries":{}},"sources":{"../guard/src/GuardV0Base.sol":{"keccak256":"0x33104cff571c90635cd59d2566b4e4d4bd4afdf1c8c7e10253aed898dd63f2e6","urls":["bzz-raw://0eeca7f95edb6e3d6e20db92d39aa0a19170d661619d9aa24fb05bf19d86a897","dweb:/ipfs/QmdULXJcgqLEgXgXRwKbuvonGq3vRPv9Bnh61p4ZpFt4JJ"],"license":null},"../guard/src/IGuard.sol":{"keccak256":"0xcb7ae06b3f3406e29c4012a1ba0ff81a8d30e6e760c15241e9182069c34db1c4","urls":["bzz-raw://2ef7e5cc3383a1439396e6c9ff0821b60e461c502d90d83b81b104ad88362f6a","dweb:/ipfs/QmXQWP9shBmdD219btryxgfhY3RExjASgofb41SpAGdWhW"],"license":null},"../guard/src/lib/BytesLib.sol":{"keccak256":"0x7446c0f0de6eb147f0f3e40b3a72e39d02a951e8c453437dcc6b3c06007bc62a","urls":["bzz-raw://1b2b64668ea4287dfe75e1029fef387b0e3ae191b784d1bc5be01edac6d1d5c5","dweb:/ipfs/Qma3zAe3Svez1kdGc5F356m9tRjCDg8F8tAocbd5Qk13PE"],"license":"Unlicense"},"../guard/src/lib/Path.sol":{"keccak256":"0xb22c562b5175d50dbcc2224325666090d985f052abdcfe275c8dfc884e34de61","urls":["bzz-raw://27aeff27314476ab3c77313ae1f0c5b2442b86315f5ce741cb2150a87c3cbad3","dweb:/ipfs/QmQNno2g1CJvjnH6ych4LuHr1UZDEZPACgYG4XvivxGrqV"],"license":"GPL-2.0-or-later"},"lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol":{"keccak256":"0xc163fcf9bb10138631a9ba5564df1fa25db9adff73bd9ee868a8ae1858fe093a","urls":["bzz-raw://9706d43a0124053d9880f6e31a59f31bc0a6a3dc1acd66ce0a16e1111658c5f6","dweb:/ipfs/QmUFmfowzkRwGtDu36cXV9SPTBHJ3n7dG9xQiK5B28jTf2"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol":{"keccak256":"0x631188737069917d2f909d29ce62c4d48611d326686ba6683e26b72a23bfac0b","urls":["bzz-raw://7a61054ae84cd6c4d04c0c4450ba1d6de41e27e0a2c4f1bcdf58f796b401c609","dweb:/ipfs/QmUvtdp7X1mRVyC3CsHrtPbgoqWaXHp3S1ZR24tpAQYJWM"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol":{"keccak256":"0xdbef5f0c787055227243a7318ef74c8a5a1108ca3a07f2b3a00ef67769e1e397","urls":["bzz-raw://08e39f23d5b4692f9a40803e53a8156b72b4c1f9902a88cd65ba964db103dab9","dweb:/ipfs/QmPKn6EYDgpga7KtpkA8wV2yJCYGMtc9K4LkJfhKX2RVSV"],"license":"MIT"},"lib/safe-contracts/contracts/common/Enum.sol":{"keccak256":"0x473e45b1a5cc47be494b0e123c9127f0c11c1e0992a321ae5a644c0bfdb2c14f","urls":["bzz-raw://948e6d5a8bd7377f7795b6e28584eab9be5c0a8db240bf5e606744a175238c3d","dweb:/ipfs/QmQQaUmQfpuejV41jbxKSTgEBYYCcVTjCsDMgf2htgz6ej"],"license":"LGPL-3.0-only"},"lib/zodiac/contracts/core/Module.sol":{"keccak256":"0xdf5df30dee53582cbdfce3aa4a860ed3326062a5806b82fd99c21854b329bda4","urls":["bzz-raw://7e25ef9cbfb973a3ae8237f8ea06772378bba1a2bc0c4f3b913f2a54dbb12525","dweb:/ipfs/Qmcc3ozbfjAkQtbXqq2DSeL3nJs4rNqC9LEpdkWJ4tFcNS"],"license":"LGPL-3.0-only"},"lib/zodiac/contracts/factory/FactoryFriendly.sol":{"keccak256":"0xdc17580aaefd994be56dced6c9765695807eeb996b05cbd1516171ca0784d83d","urls":["bzz-raw://58402770f94d469903388ef93aca179f53638d64c76d961b8d86d3042212f0fd","dweb:/ipfs/QmZFunrkQsX3765bpbRporGc7ZBysKdR3yryvzPuEKszNY"],"license":"LGPL-3.0-only"},"lib/zodiac/contracts/interfaces/IAvatar.sol":{"keccak256":"0xb7dfa794e2ceec98635b3c0e6bb810e6001db4619c5f024413bdb9575efea6bf","urls":["bzz-raw://5900e65d1580f648b16f27d913fa000868af1919b5326155e2fd13a73691c893","dweb:/ipfs/QmUujxEr28S93i3bVYvhhuXCAb8P44ra8UCzPXcQXZbbka"],"license":"LGPL-3.0-only"},"src/TradingStrategyModuleV0.sol":{"keccak256":"0xa7f049be9d7c61aa82467f69cd213cf517942f15b73d2bf11acdd7d6694eefb6","urls":["bzz-raw://4400085916d42de23c1eb576a95e62d573f010a072d2774f396463d756d2c868","dweb:/ipfs/QmNuMqWVbaAC6dvjumq5W7ypcDUZ1t9kVfd6z4RptBcjB8"],"license":null}},"version":1},"id":14} \ No newline at end of file diff --git a/tests/lagoon/conftest.py b/tests/lagoon/conftest.py index fd734b6a..7f001f4d 100644 --- a/tests/lagoon/conftest.py +++ b/tests/lagoon/conftest.py @@ -53,6 +53,7 @@ def safe_address() -> HexAddress: return "0x20415f3Ec0FEA974548184bdD6e67575D128953F" + @pytest.fixture() def anvil_base_fork(request, vault_owner, usdc_holder, asset_manager, valuation_manager) -> AnvilLaunch: """Create a testable fork of live BNB chain. diff --git a/tests/safe-integration/test_guard_safe_e2e.py b/tests/safe-integration/test_guard_safe_e2e.py new file mode 100644 index 00000000..7cd8791e --- /dev/null +++ b/tests/safe-integration/test_guard_safe_e2e.py @@ -0,0 +1,380 @@ +"""Base mainnet fork based interation tests for TradingStrategyModuleV0 Safe module integration.""" +import os + +import pytest + +from eth_typing import HexAddress +from safe_eth.safe import Safe +from safe_eth.safe.safe import SafeV141 +from web3 import Web3 +from web3.contract import Contract +from web3.middleware import construct_sign_and_send_raw_middleware + +from eth_defi.deploy import deploy_contract +from eth_defi.hotwallet import HotWallet +from eth_defi.provider.anvil import fork_network_anvil, AnvilLaunch +from eth_defi.provider.multi_provider import create_multi_provider_web3 +from eth_defi.safe.safe_compat import create_safe_ethereum_client +from eth_defi.simple_vault.transact import encode_simple_vault_transaction +from eth_defi.token import TokenDetails, fetch_erc20_details +from eth_defi.trace import assert_transaction_success_with_explanation +from eth_defi.uniswap_v2.constants import UNISWAP_V2_DEPLOYMENTS +from eth_defi.uniswap_v2.deployment import fetch_deployment, UniswapV2Deployment, FOREVER_DEADLINE + +JSON_RPC_BASE = os.environ.get("JSON_RPC_BASE") + +CI = os.environ.get("CI", None) is not None + +pytestmark = pytest.mark.skipif(not JSON_RPC_BASE, reason="No JSON_RPC_BASE environment variable") + + +@pytest.fixture() +def deployer(web3) -> HexAddress: + """Role of who can deploy contracts""" + return web3.eth.accounts[0] + + +@pytest.fixture() +def asset_manager(web3) -> HexAddress: + """Role who can perform trades""" + return web3.eth.accounts[1] + + +@pytest.fixture() +def attacker_account(web3) -> HexAddress: + """Unauthorised account, without roles""" + return web3.eth.accounts[2] + + +@pytest.fixture() +def safe_deployer_hot_wallet(web3) -> HotWallet: + """Safe Python library only takes LocalAccount as the input for Safe.create()""" + hot_wallet = HotWallet.create_for_testing(web3) + web3.middleware_onion.add(construct_sign_and_send_raw_middleware(hot_wallet.account)) + return hot_wallet + + +@pytest.fixture() +def usdc_whale() -> HexAddress: + """Large USDC holder onchain, unlocked in Anvil for testing""" + # https://basescan.org/token/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913#balances + return "0x3304E22DDaa22bCdC5fCa2269b418046aE7b566A" + + +@pytest.fixture() +def base_usdc(web3) -> TokenDetails: + return fetch_erc20_details( + web3, + "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + ) + +@pytest.fixture() +def base_weth(web3) -> TokenDetails: + return fetch_erc20_details( + web3, + "0x4200000000000000000000000000000000000006", + ) + + +@pytest.fixture() +def uniswap_v2(web3) -> UniswapV2Deployment: + return fetch_deployment( + web3, + factory_address=UNISWAP_V2_DEPLOYMENTS["base"]["factory"], + router_address=UNISWAP_V2_DEPLOYMENTS["base"]["router"], + init_code_hash=UNISWAP_V2_DEPLOYMENTS["base"]["init_code_hash"], + ) + + +@pytest.fixture() +def anvil_base_fork(request, usdc_whale) -> AnvilLaunch: + """Create a testable fork of live BNB chain. + + :return: JSON-RPC URL for Web3 + """ + assert JSON_RPC_BASE, "JSON_RPC_BASE not set" + launch = fork_network_anvil( + JSON_RPC_BASE, + unlocked_addresses=[usdc_whale], + ) + try: + yield launch + finally: + # Wind down Anvil process after the test is complete + launch.close() + + +@pytest.fixture() +def web3(anvil_base_fork) -> Web3: + """Create a web3 connector. + + - By default use Anvil forked Base + + - Optionally enable Tenderly testnet with `JSON_RPC_TENDERLY` to debug + otherwise impossible to debug Gnosis Safe transactions + """ + + tenderly_fork_rpc = os.environ.get("JSON_RPC_TENDERLY", None) + + if tenderly_fork_rpc: + web3 = create_multi_provider_web3(tenderly_fork_rpc) + else: + web3 = create_multi_provider_web3( + anvil_base_fork.json_rpc_url, + default_http_timeout=(3, 250.0), # multicall slow, so allow improved timeout + ) + assert web3.eth.chain_id == 8453 + return web3 + + +@pytest.fixture() +def safe(web3, deployer, safe_deployer_hot_wallet) -> Safe: + """Deploy a Safe on the forked chain. + + - Use Safe version v1.4.1 + + - 1 of 1 multisig + + - safe_deployer set as the sole owner + """ + ethereum_client = create_safe_ethereum_client(web3) + owners = [safe_deployer_hot_wallet.address] + threshold = 1 + + # Safe 1.4.1 + # https://help.safe.global/en/articles/40834-verify-safe-creation + # https://basescan.org/address/0x41675C099F32341bf84BFc5382aF534df5C7461a + master_copy_address = "0x41675C099F32341bf84BFc5382aF534df5C7461a" + + safe_tx = SafeV141.create( + ethereum_client, + safe_deployer_hot_wallet.account, + master_copy_address, + owners, + threshold, + ) + contract_address = safe_tx.contract_address + safe = SafeV141(contract_address, ethereum_client) + retrieved_owners = safe.retrieve_owners() + assert retrieved_owners == owners + return safe + + +@pytest.fixture() +def uniswap_v2_whitelisted_trading_strategy_module( + web3: Web3, + safe: Safe, + safe_deployer_hot_wallet: HotWallet, + deployer: HexAddress, + asset_manager: HexAddress, + uniswap_v2: UniswapV2Deployment, + base_weth: TokenDetails, + base_usdc: TokenDetails, +) -> Contract: + """Enable TradingStrategyModuleV0 that enables trding of a single pair on Uniswap v2. + + - We set up the permissions using the owner role + + - Whitelist only USDC, WETH tokens, single trading pair on Uniswap v2 on Base + + - TradingStrategyModuleV0 is owner by the deployer until the ownership is reliquished at the end + """ + + owner = deployer + + # Deploy guard module + module = deploy_contract( + web3, + "safe-integration/TradingStrategyModuleV0.json", + deployer, + owner, + safe.address, + ) + + # Enable Safe module + # Multisig owners can enable the module + tx = safe.contract.functions.enableModule(module.address).build_transaction( + {"from": safe_deployer_hot_wallet.address, "gas": 0, "gasPrice": 0} + ) + safe_tx = safe.build_multisig_tx(safe.address, 0, tx["data"]) + safe_tx.sign(safe_deployer_hot_wallet.private_key.hex()) + tx_hash, tx = safe_tx.execute( + tx_sender_private_key=safe_deployer_hot_wallet.private_key.hex(), + ) + assert_transaction_success_with_explanation(web3, tx_hash) + + # Enable asset_manager as the whitelisted trade-executor + tx_hash = module.functions.allowSender(asset_manager, "Whitelist trade-executor").transact({"from": owner}) + assert_transaction_success_with_explanation(web3, tx_hash) + + # Enable safe as the receiver of tokens + tx_hash = module.functions.allowReceiver(safe.address, "Whitelist Safe as trade receiver").transact({"from": owner}) + assert_transaction_success_with_explanation(web3, tx_hash) + + # Whitelist tokens + module.functions.whitelistToken(base_usdc.address, "Allow USDC").transact({"from": owner}) + module.functions.whitelistToken(base_weth.address, "Allow WETH").transact({"from": owner}) + + # Whitelist Uniswap v2 + tx_hash = module.functions.whitelistUniswapV2Router(uniswap_v2.router.address, "Allow Uniswap v2").transact({"from": owner}) + assert_transaction_success_with_explanation(web3, tx_hash) + + # Relinquish ownership + tx_hash = module.functions.transferOwnership(safe.address).transact({"from": owner}) + assert_transaction_success_with_explanation(web3, tx_hash) + + return module + + +def test_enable_safe_module( + web3: Web3, + safe: Safe, + safe_deployer_hot_wallet: HotWallet, + deployer: HexAddress, +): + """Enable TradingStrategyModuleV0 module on Safe.""" + + safe_contract = safe.contract + + # Deploy guard module + module = deploy_contract( + web3, + "safe-integration/TradingStrategyModuleV0.json", + deployer, + safe.address, + safe.address, + ) + + # Multisig owners can enable the module + tx = safe_contract.functions.enableModule(module.address).build_transaction( + {"from": safe_deployer_hot_wallet.address, "gas": 0, "gasPrice": 0} + ) + safe_tx = safe.build_multisig_tx(safe.address, 0, tx["data"]) + safe_tx.sign(safe_deployer_hot_wallet.private_key.hex()) + tx_hash, tx = safe_tx.execute( + tx_sender_private_key=safe_deployer_hot_wallet.private_key.hex(), + ) + assert_transaction_success_with_explanation(web3, tx_hash) + + modules = safe.retrieve_modules() + assert modules == [module.address] + + +def test_swap_through_module_succeed( + web3: Web3, + safe: Safe, + safe_deployer_hot_wallet: HotWallet, + deployer: HexAddress, + asset_manager: HexAddress, + base_usdc: TokenDetails, + base_weth: TokenDetails, + uniswap_v2: UniswapV2Deployment, + uniswap_v2_whitelisted_trading_strategy_module, + usdc_whale: HexAddress, +): + """Perform Uniswap v2 swap using TradingStrategyModuleV0.""" + + ts_module = uniswap_v2_whitelisted_trading_strategy_module + assert safe.retrieve_modules() == [ts_module.address] + + usdc = base_usdc.contract + weth = base_weth.contract + usdc_amount = 10_000 * 10**6 + usdc.functions.transfer(safe.address, usdc_amount).transact({"from": usdc_whale}) + + path = [usdc.address, weth.address] + + approve_call = usdc.functions.approve( + uniswap_v2.router.address, + usdc_amount, + ) + + target, call_data = encode_simple_vault_transaction(approve_call) + tx_hash = ts_module.functions.performCall(target, call_data).transact({"from": asset_manager}) + assert_transaction_success_with_explanation(web3, tx_hash) + + assert weth.functions.balanceOf(safe.address).call() == 0 + + trade_call = uniswap_v2.router.functions.swapExactTokensForTokens( + usdc_amount, + 0, + path, + safe.address, + FOREVER_DEADLINE, + ) + target, call_data = encode_simple_vault_transaction(trade_call) + tx_hash = ts_module.functions.performCall(target, call_data).transact({"from": asset_manager}) + assert_transaction_success_with_explanation(web3, tx_hash) + + assert weth.functions.balanceOf(safe.address).call() > 0 + + +def test_swap_through_module_revert( + web3: Web3, + safe: Safe, + safe_deployer_hot_wallet: HotWallet, + deployer: HexAddress, + asset_manager: HexAddress, + base_usdc: TokenDetails, + base_weth: TokenDetails, + uniswap_v2: UniswapV2Deployment, + uniswap_v2_whitelisted_trading_strategy_module, + usdc_whale: HexAddress, +): + """Swap reverts (token not approved)""" + + ts_module = uniswap_v2_whitelisted_trading_strategy_module + assert safe.retrieve_modules() == [ts_module.address] + + usdc = base_usdc.contract + weth = base_weth.contract + usdc_amount = 10_000 * 10**6 + usdc.functions.transfer(safe.address, usdc_amount).transact({"from": usdc_whale}) + + path = [usdc.address, weth.address] + + trade_call = uniswap_v2.router.functions.swapExactTokensForTokens( + usdc_amount, + 0, + path, + safe.address, + FOREVER_DEADLINE, + ) + target, call_data = encode_simple_vault_transaction(trade_call) + + with pytest.raises(ValueError) as e: + ts_module.functions.performCall(target, call_data).transact({"from": asset_manager}) + + assert "TRANSFER_FROM_FAILED" in str(e) + + +def test_swap_through_module_unauthorised( + web3: Web3, + safe: Safe, + safe_deployer_hot_wallet: HotWallet, + deployer: HexAddress, + asset_manager: HexAddress, + base_usdc: TokenDetails, + base_weth: TokenDetails, + uniswap_v2: UniswapV2Deployment, + uniswap_v2_whitelisted_trading_strategy_module, + usdc_whale: HexAddress, + attacker_account: HexAddress, +): + """Operation initiated by someone that is not trade-executor""" + + ts_module = uniswap_v2_whitelisted_trading_strategy_module + assert safe.retrieve_modules() == [ts_module.address] + + usdc = base_usdc.contract + usdc_amount = 10_000 * 10**6 + + approve_call = usdc.functions.approve( + uniswap_v2.router.address, + usdc_amount, + ) + + target, call_data = encode_simple_vault_transaction(approve_call) + with pytest.raises(ValueError) as e: + ts_module.functions.performCall(target, call_data).transact({"from": attacker_account}) + assert "validateCall: Sender not allowed" in str(e) diff --git a/tests/safe-integration/test_guard_safe_uniswap_v2.py b/tests/safe-integration/test_guard_safe_uniswap_v2.py new file mode 100644 index 00000000..3d81b370 --- /dev/null +++ b/tests/safe-integration/test_guard_safe_uniswap_v2.py @@ -0,0 +1,277 @@ +"""Check Safe TradingStrategyModuleV0 against Uniswap v2 trades. + +- Check Uniswap v2 access rights + +- Check we can perform swap through TradingStrategyModuleV0 on behalf of Safe users +""" + +import pytest +from web3 import Web3, HTTPProvider +from web3._utils.events import EventLogErrorFlags +from web3.contract import Contract + +from eth_defi.abi import get_deployed_contract, get_function_selector +from eth_defi.deploy import deploy_contract +from eth_defi.provider.anvil import AnvilLaunch, launch_anvil +from eth_defi.simple_vault.transact import encode_simple_vault_transaction +from eth_defi.token import create_token +from eth_defi.trace import assert_transaction_success_with_explanation +from eth_defi.uniswap_v2.deployment import ( + FOREVER_DEADLINE, + UniswapV2Deployment, + deploy_trading_pair, + deploy_uniswap_v2_like, +) +from eth_defi.uniswap_v2.pair import PairDetails, fetch_pair_details + + +@pytest.fixture() +def anvil(request) -> AnvilLaunch: + """Create a standalone Anvil RPC backend. + + :return: JSON-RPC URL for Web3 + """ + launch = launch_anvil() + try: + yield launch + finally: + # Wind down Anvil process after the test is complete + launch.close() + + +@pytest.fixture +def web3(anvil): + """Set up a local unit testing blockchain.""" + # https://web3py.readthedocs.io/en/stable/examples.html#contract-unit-tests-in-python + return Web3(HTTPProvider(anvil.json_rpc_url)) + + +@pytest.fixture() +def deployer(web3) -> str: + """Deploy account. + + Do some account allocation for tests. + """ + return web3.eth.accounts[0] + + +@pytest.fixture() +def owner(web3) -> str: + return web3.eth.accounts[1] + + +@pytest.fixture() +def asset_manager(web3) -> str: + return web3.eth.accounts[2] + + +@pytest.fixture() +def third_party(web3) -> str: + return web3.eth.accounts[3] + + +@pytest.fixture() +def usdc(web3, deployer) -> Contract: + """Mock USDC token. + + Note that this token has 18 decimals instead of 6 of real USDC. + """ + token = create_token(web3, deployer, "USD Coin", "USDC", 100_000_000 * 10**6) + return token + + +@pytest.fixture() +def shitcoin(web3, deployer) -> Contract: + """Mock USDC token. + + Note that this token has 18 decimals instead of 6 of real USDC. + """ + token = create_token(web3, deployer, "Shitcoin", "SCAM", 1_000_000_000 * 10**18) + return token + + +@pytest.fixture() +def uniswap_v2(web3: Web3, usdc: Contract, deployer: str) -> UniswapV2Deployment: + """Deploy mock Uniswap v2.""" + balance = web3.eth.get_balance(deployer) + assert balance > 10 * 10**18 + return deploy_uniswap_v2_like(web3, deployer, give_weth=5) + + +@pytest.fixture() +def safe( + web3: Web3, + usdc: Contract, + deployer: str, + owner: str, + asset_manager: str, + uniswap_v2: UniswapV2Deployment, +) -> Contract: + """Deploy MockSafe. + + - Has ``enableModule`` and ``module`` functions + """ + weth = uniswap_v2.weth + safe = deploy_contract(web3, "safe-integration/MockSafe.json", deployer) + + # The module has ten variables that must be set: + # + # Owner: Address that can call setter functions + # Avatar: Address of the DAO (e.g a Gnosis Safe) + # Target: Address on which the module will call execModuleTransaction() + guard = deploy_contract( + web3, + "safe-integration/TradingStrategyModuleV0.json", + owner, + owner, + safe.address, + ) + + assert guard.functions.owner().call() == owner + assert guard.functions.avatar().call() == safe.address + assert guard.functions.target().call() == safe.address + tx_hash = guard.functions.whitelistUniswapV2Router(uniswap_v2.router.address, "Allow Uniswap v2").transact({"from": owner}) + receipt = web3.eth.get_transaction_receipt(tx_hash) + + assert len(receipt["logs"]) == 2 + + # Enable Safe module + tx_hash = safe.functions.enableModule(guard.address).transact({"from": owner}) + assert_transaction_success_with_explanation(web3, tx_hash) + + # Enable asset_manager as the whitelisted trade-executor + tx_hash = guard.functions.allowSender(asset_manager, "Whitelist trade-executor").transact({"from": owner}) + assert_transaction_success_with_explanation(web3, tx_hash) + + # Enable safe as the receiver of tokens + tx_hash = guard.functions.allowReceiver(safe.address, "Whitelist Safe as trade receiver").transact({"from": owner}) + assert_transaction_success_with_explanation(web3, tx_hash) + + # Check Uniswap router call sites was enabled in the receipt + call_site_events = guard.events.CallSiteApproved().process_receipt(receipt, errors=EventLogErrorFlags.Ignore) + router_selector = get_function_selector(uniswap_v2.router.functions.swapExactTokensForTokens) + assert call_site_events[0]["args"]["notes"] == "Allow Uniswap v2" + assert call_site_events[0]["args"]["selector"].hex() == router_selector.hex() + assert call_site_events[0]["args"]["target"] == uniswap_v2.router.address + + assert guard.functions.isAllowedCallSite(uniswap_v2.router.address, get_function_selector(uniswap_v2.router.functions.swapExactTokensForTokens)).call() + guard.functions.whitelistToken(usdc.address, "Allow USDC").transact({"from": owner}) + guard.functions.whitelistToken(weth.address, "Allow WETH").transact({"from": owner}) + assert guard.functions.callSiteCount().call() == 5 + return safe + + +@pytest.fixture() +def guard(web3: Web3, safe: Contract, uniswap_v2) -> Contract: + guard = get_deployed_contract(web3, "safe-integration/TradingStrategyModuleV0.json", safe.functions.module().call()) + assert guard.functions.isAllowedCallSite(uniswap_v2.router.address, get_function_selector(uniswap_v2.router.functions.swapExactTokensForTokens)).call() + return guard + + +@pytest.fixture() +def weth(uniswap_v2) -> Contract: + return uniswap_v2.weth + + +@pytest.fixture() +def weth_usdc_pair(web3, uniswap_v2, weth, usdc, deployer) -> PairDetails: + pair_address = deploy_trading_pair( + web3, + deployer, + uniswap_v2, + weth, + usdc, + 4 * 10**18, # 4 ETH liquidity + 4000 * 10**6, # 4000 USDC liquidity + ) + return fetch_pair_details(web3, pair_address) + + +@pytest.fixture() +def shitcoin_usdc_pair( + web3, + uniswap_v2, + shitcoin: Contract, + usdc: Contract, + deployer: str, +) -> PairDetails: + pair_address = deploy_trading_pair( + web3, + deployer, + uniswap_v2, + shitcoin, + usdc, + 5 * 10**18, + 10 * 10**6, + ) + return fetch_pair_details(web3, pair_address) + + +def test_safe_module_initialised( + owner: str, + asset_manager: str, + safe: Contract, + guard: Contract, + uniswap_v2: UniswapV2Deployment, + usdc: Contract, + weth: Contract, +): + """Vault and guard are initialised for the owner.""" + assert guard.functions.owner().call() == owner + assert guard.functions.isAllowedSender(asset_manager).call() is True + assert guard.functions.isAllowedSender(safe.address).call() is False + + # We have accessed needed for a swap + assert guard.functions.callSiteCount().call() == 5 + assert guard.functions.isAllowedApprovalDestination(uniswap_v2.router.address) + assert guard.functions.isAllowedCallSite(uniswap_v2.router.address, get_function_selector(uniswap_v2.router.functions.swapExactTokensForTokens)).call() + assert guard.functions.isAllowedCallSite(usdc.address, get_function_selector(usdc.functions.approve)).call() + assert guard.functions.isAllowedCallSite(usdc.address, get_function_selector(usdc.functions.transfer)).call() + assert guard.functions.isAllowedAsset(usdc.address).call() + assert guard.functions.isAllowedAsset(weth.address).call() + + +@pytest.mark.skip(reason="MockSafe integration does not behave, instead use tests against real Gnosis Safe") +def test_safe_module_can_trade_uniswap_v2( + web3: Web3, + uniswap_v2: UniswapV2Deployment, + weth_usdc_pair: PairDetails, + owner: str, + asset_manager: str, + deployer: str, + weth: Contract, + usdc: Contract, + safe: Contract, + guard: Contract, +): + """Asset manager can perform a swap. + + - Use TradingStrategyModuleV0 to perform a swap on behalf of Safe users + """ + usdc_amount = 10_000 * 10**6 + usdc.functions.transfer(safe.address, usdc_amount).transact({"from": deployer}) + + path = [usdc.address, weth.address] + + approve_call = usdc.functions.approve( + uniswap_v2.router.address, + usdc_amount, + ) + + target, call_data = encode_simple_vault_transaction(approve_call) + tx_hash = guard.functions.performCall(target, call_data).transact({"from": asset_manager}) + assert_transaction_success_with_explanation(web3, tx_hash) + + trade_call = uniswap_v2.router.functions.swapExactTokensForTokens( + usdc_amount, + 0, + path, + safe.address, + FOREVER_DEADLINE, + ) + target, call_data = encode_simple_vault_transaction(trade_call) + tx_hash = guard.functions.performCall(target, call_data).transact({"from": asset_manager}) + assert_transaction_success_with_explanation(web3, tx_hash) + + assert weth.functions.balanceOf(safe.address).call() == 3696700037078235076 +