ETH Price: $2,283.80 (+4.97%)

Transaction Decoder

Block:
19134891 at Feb-01-2024 04:53:11 PM +UTC
Transaction Fee:
0.01158136575513845 ETH $26.45
Gas Used:
213,982 Gas / 54.123083975 Gwei

Emitted Events:

227 AppProxyUpgradeable.0x96a25c8ce0baabc1fdefd93e9ed25d8e092a3332f3aa9a41722b5697231d1d1a( 0x96a25c8ce0baabc1fdefd93e9ed25d8e092a3332f3aa9a41722b5697231d1d1a, 0x0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0, 0000000000000000000000000000000000000000000000001bc16d674ec80000, 0000000000000000000000000000000000000000000000000000000000000000 )
228 AppProxyUpgradeable.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0, 0000000000000000000000000000000000000000000000001bc16d674ec7ffff )
229 AppProxyUpgradeable.0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb( 0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0, 00000000000000000000000000000000000000000000000018045d9873b78756 )
230 WstETH.Transfer( from=0x0000000000000000000000000000000000000000, to=0xE37e799D5077682FA0a244D46E5649F71457BD09, value=1730611066174932822 )
231 WstETH.Transfer( from=0xE37e799D5077682FA0a244D46E5649F71457BD09, to=0x0000000000000000000000000000000000000000, value=1730611066174932822 )
232 AppProxyUpgradeable.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0, 0x000000000000000000000000e37e799d5077682fa0a244d46e5649f71457bd09, 0000000000000000000000000000000000000000000000001bc16d674ec7ffff )
233 AppProxyUpgradeable.0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb( 0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb, 0x0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0, 0x000000000000000000000000e37e799d5077682fa0a244d46e5649f71457bd09, 00000000000000000000000000000000000000000000000018045d9873b78755 )
234 AppProxyUpgradeable.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000e37e799d5077682fa0a244d46e5649f71457bd09, 0x0000000000000000000000001111111254eeb25477b68fb85ed929f73a960582, 0000000000000000000000000000000000000000000000001bc16d674ec7fffe )
235 AppProxyUpgradeable.0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb( 0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb, 0x000000000000000000000000e37e799d5077682fa0a244d46e5649f71457bd09, 0x0000000000000000000000001111111254eeb25477b68fb85ed929f73a960582, 00000000000000000000000000000000000000000000000018045d9873b78754 )
236 AppProxyUpgradeable.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000001111111254eeb25477b68fb85ed929f73a960582, 0x0000000000000000000000006e7c518ec6f487293321d04488447b831be03209, 0000000000000000000000000000000000000000000000001bc16d674ec7fffe )
237 AppProxyUpgradeable.0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb( 0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb, 0x0000000000000000000000001111111254eeb25477b68fb85ed929f73a960582, 0x0000000000000000000000006e7c518ec6f487293321d04488447b831be03209, 00000000000000000000000000000000000000000000000018045d9873b78754 )

Account State Difference:

  Address   Before After State Difference Code
0x6e7C518e...31BE03209
4.051067589411962334 Eth
Nonce: 48
2.039486223656823884 Eth
Nonce: 49
2.01158136575513845
(beaverbuild)
10.224214303501325693 Eth10.224235701701325693 Eth0.0000213982
0xae7ab965...312D7fE84 9,056.023358036013829631 Eth9,058.023358036013829631 Eth2

Execution Trace

ETH 2 AggregationRouterV5.swap( executor=0xE37e799D5077682FA0a244D46E5649F71457BD09, desc=[{name:srcToken, type:address, order:1, indexed:false, value:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, valueString:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE}, {name:dstToken, type:address, order:2, indexed:false, value:0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84, valueString:0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84}, {name:srcReceiver, type:address, order:3, indexed:false, value:0xE37e799D5077682FA0a244D46E5649F71457BD09, valueString:0xE37e799D5077682FA0a244D46E5649F71457BD09}, {name:dstReceiver, type:address, order:4, indexed:false, value:0x6e7C518eC6F487293321d04488447b831BE03209, valueString:0x6e7C518eC6F487293321d04488447b831BE03209}, {name:amount, type:uint256, order:5, indexed:false, value:2000000000000000000, valueString:2000000000000000000}, {name:minReturnAmount, type:uint256, order:6, indexed:false, value:1998999999999999998, valueString:1998999999999999998}, {name:flags, type:uint256, order:7, indexed:false, value:0, valueString:0}], permit=0x, data=0x0000000000000000000000000000000000000000000001220000F40000AA00A007E5C0D200000000000000000000000000000000000000008600006C00003000001640607F39C581F595B53C5CB19BD0B3F8DA6C935E2CA00020D6BDBF787F39C581F595B53C5CB19BD0B3F8DA6C935E2CA041207F39C581F595B53C5CB19BD0B3F8DA6C935E2CA00004DE0E9A3E00000000000000000000000000000000000000000000000000000000000000000020D6BDBF78AE7AB96520DE3A18E5E111B5EAAB095312D7FE8400A0F2FA6B66AE7AB96520DE3A18E5E111B5EAAB095312D7FE840000000000000000000000000000000000000000000000001BC16D674EC7FFFE00000000000000000007CB78C1CA11F580A06C4ECA27AE7AB96520DE3A18E5E111B5EAAB095312D7FE841111111254EEB25477B68FB85ED929F73A960582 ) => ( returnAmount=1999999999999999998, spentAmount=2000000000000000000 )
  • ETH 2 0xe37e799d5077682fa0a244d46e5649f71457bd09.4b64e492( )
    • 0xe37e799d5077682fa0a244d46e5649f71457bd09.07e5c0d2( )
      • ETH 2 WstETH.CALL( )
        • ETH 2 AppProxyUpgradeable.a1903eab( )
          • KernelProxy.be00bbd8( )
            • Kernel.getApp( _namespace=F1F3EB40F5BC1AD1344716CED8B8A0431D840B5783AEA1FD01786BC26F35AC0F, _appId=3CA7C3E38968823CCB4C78EA688DF41356F182AE1D159E4EE608D30D68CEF320 ) => ( 0x17144556fd3424EDC8Fc8A4C940B2D04936d17eb )
            • ETH 2 Lido.submit( _referral=0x0000000000000000000000000000000000000000 ) => ( 1730611066174932822 )
            • 0xe37e799d5077682fa0a244d46e5649f71457bd09.d6bdbf78( )
              • WstETH.balanceOf( account=0xE37e799D5077682FA0a244D46E5649F71457BD09 ) => ( 1730611066174932823 )
              • WstETH.unwrap( _wstETHAmount=1730611066174932822 ) => ( 1999999999999999999 )
                • AppProxyUpgradeable.7a28fb88( )
                  • KernelProxy.be00bbd8( )
                    • Kernel.getApp( _namespace=F1F3EB40F5BC1AD1344716CED8B8A0431D840B5783AEA1FD01786BC26F35AC0F, _appId=3CA7C3E38968823CCB4C78EA688DF41356F182AE1D159E4EE608D30D68CEF320 ) => ( 0x17144556fd3424EDC8Fc8A4C940B2D04936d17eb )
                    • Lido.getPooledEthByShares( _sharesAmount=1730611066174932822 ) => ( 1999999999999999999 )
                    • AppProxyUpgradeable.a9059cbb( )
                      • KernelProxy.be00bbd8( )
                        • Kernel.getApp( _namespace=F1F3EB40F5BC1AD1344716CED8B8A0431D840B5783AEA1FD01786BC26F35AC0F, _appId=3CA7C3E38968823CCB4C78EA688DF41356F182AE1D159E4EE608D30D68CEF320 ) => ( 0x17144556fd3424EDC8Fc8A4C940B2D04936d17eb )
                        • Lido.transfer( _recipient=0xE37e799D5077682FA0a244D46E5649F71457BD09, _amount=1999999999999999999 ) => ( True )
                        • 0xe37e799d5077682fa0a244d46e5649f71457bd09.d6bdbf78( )
                          • AppProxyUpgradeable.70a08231( )
                            • KernelProxy.be00bbd8( )
                              • Kernel.getApp( _namespace=F1F3EB40F5BC1AD1344716CED8B8A0431D840B5783AEA1FD01786BC26F35AC0F, _appId=3CA7C3E38968823CCB4C78EA688DF41356F182AE1D159E4EE608D30D68CEF320 ) => ( 0x17144556fd3424EDC8Fc8A4C940B2D04936d17eb )
                              • Lido.balanceOf( _account=0xE37e799D5077682FA0a244D46E5649F71457BD09 ) => ( 1999999999999999999 )
                              • 0xe37e799d5077682fa0a244d46e5649f71457bd09.f2fa6b66( )
                              • 0xe37e799d5077682fa0a244d46e5649f71457bd09.6c4eca27( )
                                • AppProxyUpgradeable.a9059cbb( )
                                  • KernelProxy.be00bbd8( )
                                    • Kernel.getApp( _namespace=F1F3EB40F5BC1AD1344716CED8B8A0431D840B5783AEA1FD01786BC26F35AC0F, _appId=3CA7C3E38968823CCB4C78EA688DF41356F182AE1D159E4EE608D30D68CEF320 ) => ( 0x17144556fd3424EDC8Fc8A4C940B2D04936d17eb )
                                    • Lido.transfer( _recipient=0x1111111254EEB25477B68fb85Ed929f73A960582, _amount=1999999999999999998 ) => ( True )
                                    • AppProxyUpgradeable.70a08231( )
                                      • KernelProxy.be00bbd8( )
                                        • Kernel.getApp( _namespace=F1F3EB40F5BC1AD1344716CED8B8A0431D840B5783AEA1FD01786BC26F35AC0F, _appId=3CA7C3E38968823CCB4C78EA688DF41356F182AE1D159E4EE608D30D68CEF320 ) => ( 0x17144556fd3424EDC8Fc8A4C940B2D04936d17eb )
                                        • Lido.balanceOf( _account=0x1111111254EEB25477B68fb85Ed929f73A960582 ) => ( 1999999999999999999 )
                                        • AppProxyUpgradeable.a9059cbb( )
                                          • KernelProxy.be00bbd8( )
                                            • Kernel.getApp( _namespace=F1F3EB40F5BC1AD1344716CED8B8A0431D840B5783AEA1FD01786BC26F35AC0F, _appId=3CA7C3E38968823CCB4C78EA688DF41356F182AE1D159E4EE608D30D68CEF320 ) => ( 0x17144556fd3424EDC8Fc8A4C940B2D04936d17eb )
                                            • Lido.transfer( _recipient=0x6e7C518eC6F487293321d04488447b831BE03209, _amount=1999999999999999998 ) => ( True )
                                              File 1 of 6: AggregationRouterV5
                                              /*
                                                                                                         ,▄▓▓██▌   ,╓▄▄▓▓▓▓▓▓▓▓▄▄▄,,
                                                                                                      ,▓██▓███▓▄▓███▓╬╬╬╬╬╬╬╬╬╬╬╬╬▓███▓▄,
                                                                                                ▄█   ▓██╬╣███████╬▓▀╬╬▓▓▓████████████▓█████▄,
                                                                                               ▓██▌ ▓██╬╣██████╬▓▌  ██████████████████████▌╙╙▀ⁿ
                                                                                              ▐████████╬▓████▓▓█╨ ▄ ╟█████████▓▓╬╬╬╬╬▓▓█████▓▄
                                                                                └▀▓▓▄╓        ╟█▓╣█████▓██████▀ ╓█▌ ███████▓▓▓▓▓╬╬╬╬╬╬╬╬╬╬╬╬▓██▓▄
                                                                                   └▀████▓▄╥  ▐██╬╬██████████╙ Æ▀─ ▓███▀╚╠╬╩▀▀███████▓▓╬╬╬╬╬╬╬╬╬██▄
                                                                                      └▀██▓▀▀█████▓╬▓██████▀     ▄█████▒╠"      └╙▓██████▓╬╬╬╬╬╬╬╬██▄
                                                                                         └▀██▄,└╙▀▀████▌└╙    ^"▀╙╙╙"╙██      @▄    ╙▀███████╬╬╬╬╬╬╬██µ
                                                                                            └▀██▓▄, ██▌       ╒       ╙█▓     ]▓█▓╔    ▀███████▓╬╬╬╬╬▓█▌
                                                                                                ▀█████       ▓         ╟█▌    ]╠██▓░▒╓   ▀████████╬╬╬╬╣█▌
                                                                                                ▐████      ╓█▀█▌      ,██▌    ╚Å███▓▒▒╠╓  ╙█████████╬╬╬╣█▌
                                                                                                └████     ▓█░░▓█      ▀▀▀    φ▒╫████▒▒▒▒╠╓  █████████▓╬╬▓█µ
                                                                                                 ╘███µ ▌▄█▓▄▓▀`     ,▀    ,╔╠░▓██████▌╠▒▒▒φ  ██████████╬╬██
                                                                                                 ▐████µ╙▓▀`     ,▀╙,╔╔φφφ╠░▄▓███████▌░▓╙▒▒▒╠ └██╬███████╬▓█⌐
                                                                                                 ╫██ ▓▌         ▌φ▒▒░▓██████████████▌▒░▓╚▒▒▒╠ ▓██╬▓██████╣█▌
                                                                                                 ██▌           ▌╔▒▒▄████████████████▒▒▒░▌╠▒▒▒≥▐██▓╬╬███████▌
                                                                                                 ██▌      ,╓φ╠▓«▒▒▓████▀  ▀█████████▌▒▒▒╟░▒▒▒▒▐███╬╬╣████▓█▌
                                                                                                ▐██      ╠▒▄▓▓███▓████└     ▀████████▌▒▒░▌╚▒▒▒▐███▓╬╬████ ╙▌
                                                                                                ███  )  ╠▒░░░▒░╬████▀        └████████░▒▒░╬∩▒▒▓████╬╬╣███
                                                                                               ▓██    ╠╠▒▒▐█▀▀▌`░╫██           ███████▒▒▒▒░▒▒½█████╬╬╣███
                                                                                              ███ ,█▄ ╠▒▒▒╫▌,▄▀,▒╫██           ╟██████▒▒▒░╣⌠▒▓█████╬╬╣██▌
                                                                                             ╘██µ ██` ╠▒▒░██╬φ╠▄▓██`            ██████░░▌φ╠░▓█████▓╬╬▓██
                                                                                              ╟██  .φ╠▒░▄█▀░░▄██▀└              █████▌▒╣φ▒░▓██████╬╬╣██
                                                                                               ▀██▄▄▄╓▄███████▀                ▐█████░▓φ▒▄███████▓╬╣██
                                                                                                 ╙▀▀▀██▀└                      ████▓▄▀φ▄▓████████╬▓█▀
                                                                                                                              ▓███╬╩╔╣██████████▓██└
                                                                                                                            ╓████▀▄▓████████▀████▀
                                                                                                                          ,▓███████████████─]██╙
                                                                                                                       ,▄▓██████████████▀└  ╙
                                                                                                                  ,╓▄▓███████████████▀╙
                                                                                                           `"▀▀▀████████▀▀▀▀`▄███▀▀└
                                                                                                                            └└
                                              
                                              
                                              
                                                                  11\   11\                     11\             11\   11\            11\                                       11\
                                                                1111 |  \__|                    11 |            111\  11 |           11 |                                      11 |
                                                                \_11 |  11\ 1111111\   1111111\ 1111111\        1111\ 11 | 111111\ 111111\   11\  11\  11\  111111\   111111\  11 |  11\
                                                                  11 |  11 |11  __11\ 11  _____|11  __11\       11 11\11 |11  __11\\_11  _|  11 | 11 | 11 |11  __11\ 11  __11\ 11 | 11  |
                                                                  11 |  11 |11 |  11 |11 /      11 |  11 |      11 \1111 |11111111 | 11 |    11 | 11 | 11 |11 /  11 |11 |  \__|111111  /
                                                                  11 |  11 |11 |  11 |11 |      11 |  11 |      11 |\111 |11   ____| 11 |11\ 11 | 11 | 11 |11 |  11 |11 |      11  _11<
                                                                111111\ 11 |11 |  11 |\1111111\ 11 |  11 |      11 | \11 |\1111111\  \1111  |\11111\1111  |\111111  |11 |      11 | \11\
                                                                \______|\__|\__|  \__| \_______|\__|  \__|      \__|  \__| \_______|  \____/  \_____\____/  \______/ \__|      \__|  \__|
                                              
                                              
                                              
                                                                             111111\                                                               11\     11\
                                                                            11  __11\                                                              11 |    \__|
                                                                            11 /  11 | 111111\   111111\   111111\   111111\   111111\   111111\ 111111\   11\  111111\  1111111\
                                                                            11111111 |11  __11\ 11  __11\ 11  __11\ 11  __11\ 11  __11\  \____11\\_11  _|  11 |11  __11\ 11  __11\
                                                                            11  __11 |11 /  11 |11 /  11 |11 |  \__|11111111 |11 /  11 | 1111111 | 11 |    11 |11 /  11 |11 |  11 |
                                                                            11 |  11 |11 |  11 |11 |  11 |11 |      11   ____|11 |  11 |11  __11 | 11 |11\ 11 |11 |  11 |11 |  11 |
                                                                            11 |  11 |\1111111 |\1111111 |11 |      \1111111\ \1111111 |\1111111 | \1111  |11 |\111111  |11 |  11 |
                                                                            \__|  \__| \____11 | \____11 |\__|       \_______| \____11 | \_______|  \____/ \__| \______/ \__|  \__|
                                                                                      11\   11 |11\   11 |                    11\   11 |
                                                                                      \111111  |\111111  |                    \111111  |
                                                                                       \______/  \______/                      \______/
                                                                                              1111111\                        11\
                                                                                              11  __11\                       11 |
                                                                                              11 |  11 | 111111\  11\   11\ 111111\    111111\   111111\
                                                                                              1111111  |11  __11\ 11 |  11 |\_11  _|  11  __11\ 11  __11\
                                                                                              11  __11< 11 /  11 |11 |  11 |  11 |    11111111 |11 |  \__|
                                                                                              11 |  11 |11 |  11 |11 |  11 |  11 |11\ 11   ____|11 |
                                                                                              11 |  11 |\111111  |\111111  |  \1111  |\1111111\ 11 |
                                                                                              \__|  \__| \______/  \______/    \____/  \_______|\__|
                                              */
                                              
                                              // SPDX-License-Identifier: MIT
                                              
                                              // File contracts/interfaces/IClipperExchangeInterface.sol
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              /// @title Clipper interface subset used in swaps
                                              interface IClipperExchangeInterface {
                                                  struct Signature {
                                                      uint8 v;
                                                      bytes32 r;
                                                      bytes32 s;
                                                  }
                                              
                                                  function sellEthForToken(address outputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external payable;
                                                  function sellTokenForEth(address inputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external;
                                                  function swap(address inputToken, address outputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external;
                                              }
                                              
                                              
                                              // File contracts/helpers/RouterErrors.sol
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              library RouterErrors {
                                                  error ReturnAmountIsNotEnough();
                                                  error InvalidMsgValue();
                                                  error ERC20TransferFailed();
                                              }
                                              
                                              
                                              // File @1inch/solidity-utils/contracts/[email protected]
                                              
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              abstract contract EthReceiver {
                                                  error EthDepositRejected();
                                              
                                                  receive() external payable {
                                                      _receive();
                                                  }
                                              
                                                  function _receive() internal virtual {
                                                      // solhint-disable-next-line avoid-tx-origin
                                                      if (msg.sender == tx.origin) revert EthDepositRejected();
                                                  }
                                              }
                                              
                                              
                                              // File @openzeppelin/contracts/token/ERC20/[email protected]
                                              
                                              // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              /**
                                               * @dev Interface of the ERC20 standard as defined in the EIP.
                                               */
                                              interface IERC20 {
                                                  /**
                                                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                                                   * another (`to`).
                                                   *
                                                   * Note that `value` may be zero.
                                                   */
                                                  event Transfer(address indexed from, address indexed to, uint256 value);
                                              
                                                  /**
                                                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                                                   * a call to {approve}. `value` is the new allowance.
                                                   */
                                                  event Approval(address indexed owner, address indexed spender, uint256 value);
                                              
                                                  /**
                                                   * @dev Returns the amount of tokens in existence.
                                                   */
                                                  function totalSupply() external view returns (uint256);
                                              
                                                  /**
                                                   * @dev Returns the amount of tokens owned by `account`.
                                                   */
                                                  function balanceOf(address account) external view returns (uint256);
                                              
                                                  /**
                                                   * @dev Moves `amount` tokens from the caller's account to `to`.
                                                   *
                                                   * Returns a boolean value indicating whether the operation succeeded.
                                                   *
                                                   * Emits a {Transfer} event.
                                                   */
                                                  function transfer(address to, uint256 amount) external returns (bool);
                                              
                                                  /**
                                                   * @dev Returns the remaining number of tokens that `spender` will be
                                                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                                                   * zero by default.
                                                   *
                                                   * This value changes when {approve} or {transferFrom} are called.
                                                   */
                                                  function allowance(address owner, address spender) external view returns (uint256);
                                              
                                                  /**
                                                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                                                   *
                                                   * Returns a boolean value indicating whether the operation succeeded.
                                                   *
                                                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                                                   * that someone may use both the old and the new allowance by unfortunate
                                                   * transaction ordering. One possible solution to mitigate this race
                                                   * condition is to first reduce the spender's allowance to 0 and set the
                                                   * desired value afterwards:
                                                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                                                   *
                                                   * Emits an {Approval} event.
                                                   */
                                                  function approve(address spender, uint256 amount) external returns (bool);
                                              
                                                  /**
                                                   * @dev Moves `amount` tokens from `from` to `to` using the
                                                   * allowance mechanism. `amount` is then deducted from the caller's
                                                   * allowance.
                                                   *
                                                   * Returns a boolean value indicating whether the operation succeeded.
                                                   *
                                                   * Emits a {Transfer} event.
                                                   */
                                                  function transferFrom(
                                                      address from,
                                                      address to,
                                                      uint256 amount
                                                  ) external returns (bool);
                                              }
                                              
                                              
                                              // File @1inch/solidity-utils/contracts/interfaces/[email protected]
                                              
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              
                                              interface IDaiLikePermit {
                                                  function permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s) external;
                                              }
                                              
                                              
                                              // File @1inch/solidity-utils/contracts/libraries/[email protected]
                                              
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              library RevertReasonForwarder {
                                                  function reRevert() internal pure {
                                                      // bubble up revert reason from latest external call
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let ptr := mload(0x40)
                                                          returndatacopy(ptr, 0, returndatasize())
                                                          revert(ptr, returndatasize())
                                                      }
                                                  }
                                              }
                                              
                                              
                                              // File @openzeppelin/contracts/token/ERC20/extensions/[email protected]
                                              
                                              // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              /**
                                               * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                                               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                                               *
                                               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                                               * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                                               * need to send a transaction, and thus is not required to hold Ether at all.
                                               */
                                              interface IERC20Permit {
                                                  /**
                                                   * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                                                   * given ``owner``'s signed approval.
                                                   *
                                                   * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                                                   * ordering also apply here.
                                                   *
                                                   * Emits an {Approval} event.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `spender` cannot be the zero address.
                                                   * - `deadline` must be a timestamp in the future.
                                                   * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                                                   * over the EIP712-formatted function arguments.
                                                   * - the signature must use ``owner``'s current nonce (see {nonces}).
                                                   *
                                                   * For more information on the signature format, see the
                                                   * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                                                   * section].
                                                   */
                                                  function permit(
                                                      address owner,
                                                      address spender,
                                                      uint256 value,
                                                      uint256 deadline,
                                                      uint8 v,
                                                      bytes32 r,
                                                      bytes32 s
                                                  ) external;
                                              
                                                  /**
                                                   * @dev Returns the current nonce for `owner`. This value must be
                                                   * included whenever a signature is generated for {permit}.
                                                   *
                                                   * Every successful call to {permit} increases ``owner``'s nonce by one. This
                                                   * prevents a signature from being used multiple times.
                                                   */
                                                  function nonces(address owner) external view returns (uint256);
                                              
                                                  /**
                                                   * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                                                   */
                                                  // solhint-disable-next-line func-name-mixedcase
                                                  function DOMAIN_SEPARATOR() external view returns (bytes32);
                                              }
                                              
                                              
                                              // File @1inch/solidity-utils/contracts/libraries/[email protected]
                                              
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              
                                              
                                              
                                              library SafeERC20 {
                                                  error SafeTransferFailed();
                                                  error SafeTransferFromFailed();
                                                  error ForceApproveFailed();
                                                  error SafeIncreaseAllowanceFailed();
                                                  error SafeDecreaseAllowanceFailed();
                                                  error SafePermitBadLength();
                                              
                                                  // Ensures method do not revert or return boolean `true`, admits call to non-smart-contract
                                                  function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
                                                      bytes4 selector = token.transferFrom.selector;
                                                      bool success;
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let data := mload(0x40)
                                              
                                                          mstore(data, selector)
                                                          mstore(add(data, 0x04), from)
                                                          mstore(add(data, 0x24), to)
                                                          mstore(add(data, 0x44), amount)
                                                          success := call(gas(), token, 0, data, 100, 0x0, 0x20)
                                                          if success {
                                                              switch returndatasize()
                                                              case 0 { success := gt(extcodesize(token), 0) }
                                                              default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
                                                          }
                                                      }
                                                      if (!success) revert SafeTransferFromFailed();
                                                  }
                                              
                                                  // Ensures method do not revert or return boolean `true`, admits call to non-smart-contract
                                                  function safeTransfer(IERC20 token, address to, uint256 value) internal {
                                                      if (!_makeCall(token, token.transfer.selector, to, value)) {
                                                          revert SafeTransferFailed();
                                                      }
                                                  }
                                              
                                                  // If `approve(from, to, amount)` fails, try to `approve(from, to, 0)` before retry
                                                  function forceApprove(IERC20 token, address spender, uint256 value) internal {
                                                      if (!_makeCall(token, token.approve.selector, spender, value)) {
                                                          if (!_makeCall(token, token.approve.selector, spender, 0) ||
                                                              !_makeCall(token, token.approve.selector, spender, value))
                                                          {
                                                              revert ForceApproveFailed();
                                                          }
                                                      }
                                                  }
                                              
                                                  function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                                                      uint256 allowance = token.allowance(address(this), spender);
                                                      if (value > type(uint256).max - allowance) revert SafeIncreaseAllowanceFailed();
                                                      forceApprove(token, spender, allowance + value);
                                                  }
                                              
                                                  function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                                                      uint256 allowance = token.allowance(address(this), spender);
                                                      if (value > allowance) revert SafeDecreaseAllowanceFailed();
                                                      forceApprove(token, spender, allowance - value);
                                                  }
                                              
                                                  function safePermit(IERC20 token, bytes calldata permit) internal {
                                                      bool success;
                                                      if (permit.length == 32 * 7) {
                                                          success = _makeCalldataCall(token, IERC20Permit.permit.selector, permit);
                                                      } else if (permit.length == 32 * 8) {
                                                          success = _makeCalldataCall(token, IDaiLikePermit.permit.selector, permit);
                                                      } else {
                                                          revert SafePermitBadLength();
                                                      }
                                                      if (!success) RevertReasonForwarder.reRevert();
                                                  }
                                              
                                                  function _makeCall(IERC20 token, bytes4 selector, address to, uint256 amount) private returns(bool success) {
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let data := mload(0x40)
                                              
                                                          mstore(data, selector)
                                                          mstore(add(data, 0x04), to)
                                                          mstore(add(data, 0x24), amount)
                                                          success := call(gas(), token, 0, data, 0x44, 0x0, 0x20)
                                                          if success {
                                                              switch returndatasize()
                                                              case 0 { success := gt(extcodesize(token), 0) }
                                                              default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
                                                          }
                                                      }
                                                  }
                                              
                                                  function _makeCalldataCall(IERC20 token, bytes4 selector, bytes calldata args) private returns(bool success) {
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let len := add(4, args.length)
                                                          let data := mload(0x40)
                                              
                                                          mstore(data, selector)
                                                          calldatacopy(add(data, 0x04), args.offset, args.length)
                                                          success := call(gas(), token, 0, data, len, 0x0, 0x20)
                                                          if success {
                                                              switch returndatasize()
                                                              case 0 { success := gt(extcodesize(token), 0) }
                                                              default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
                                                          }
                                                      }
                                                  }
                                              }
                                              
                                              
                                              // File @1inch/solidity-utils/contracts/interfaces/[email protected]
                                              
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              interface IWETH is IERC20 {
                                                  function deposit() external payable;
                                                  function withdraw(uint256 amount) external;
                                              }
                                              
                                              
                                              // File contracts/routers/ClipperRouter.sol
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              
                                              
                                              
                                              
                                              
                                              /// @title Clipper router that allows to use `ClipperExchangeInterface` for swaps
                                              contract ClipperRouter is EthReceiver {
                                                  using SafeERC20 for IERC20;
                                              
                                                  uint256 private constant _SIGNATURE_S_MASK = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
                                                  uint256 private constant _SIGNATURE_V_SHIFT = 255;
                                                  bytes6 private constant _INCH_TAG_WITH_LENGTH_PREFIX = "\x051INCH";
                                                  IERC20 private constant _ETH = IERC20(address(0));
                                                  IWETH private immutable _WETH;  // solhint-disable-line var-name-mixedcase
                                              
                                                  constructor(IWETH weth) {
                                                      _WETH = weth;
                                                  }
                                              
                                                  /// @notice Same as `clipperSwapTo` but calls permit first,
                                                  /// allowing to approve token spending and make a swap in one transaction.
                                                  /// @dev See tests for examples
                                                  /// @param recipient Address that will receive swap funds
                                                  /// @param srcToken Source token
                                                  /// @param dstToken Destination token
                                                  /// @param inputAmount Amount of source tokens to swap
                                                  /// @param outputAmount Amount of destination tokens to receive
                                                  /// @param goodUntil Timestamp until the swap will be valid
                                                  /// @param r Clipper order signature (r part)
                                                  /// @param vs Clipper order signature (vs part)
                                                  /// @param permit Should contain valid permit that can be used in `IERC20Permit.permit` calls.
                                                  /// @return returnAmount Amount of destination tokens received
                                                  function clipperSwapToWithPermit(
                                                      IClipperExchangeInterface clipperExchange,
                                                      address payable recipient,
                                                      IERC20 srcToken,
                                                      IERC20 dstToken,
                                                      uint256 inputAmount,
                                                      uint256 outputAmount,
                                                      uint256 goodUntil,
                                                      bytes32 r,
                                                      bytes32 vs,
                                                      bytes calldata permit
                                                  ) external returns(uint256 returnAmount) {
                                                      srcToken.safePermit(permit);
                                                      return clipperSwapTo(clipperExchange, recipient, srcToken, dstToken, inputAmount, outputAmount, goodUntil, r, vs);
                                                  }
                                              
                                                  /// @notice Same as `clipperSwapTo` but uses `msg.sender` as recipient
                                                  /// @param srcToken Source token
                                                  /// @param dstToken Destination token
                                                  /// @param inputAmount Amount of source tokens to swap
                                                  /// @param outputAmount Amount of destination tokens to receive
                                                  /// @param goodUntil Timestamp until the swap will be valid
                                                  /// @param r Clipper order signature (r part)
                                                  /// @param vs Clipper order signature (vs part)
                                                  /// @return returnAmount Amount of destination tokens received
                                                  function clipperSwap(
                                                      IClipperExchangeInterface clipperExchange,
                                                      IERC20 srcToken,
                                                      IERC20 dstToken,
                                                      uint256 inputAmount,
                                                      uint256 outputAmount,
                                                      uint256 goodUntil,
                                                      bytes32 r,
                                                      bytes32 vs
                                                  ) external payable returns(uint256 returnAmount) {
                                                      return clipperSwapTo(clipperExchange, payable(msg.sender), srcToken, dstToken, inputAmount, outputAmount, goodUntil, r, vs);
                                                  }
                                              
                                                  /// @notice Performs swap using Clipper exchange. Wraps and unwraps ETH if required.
                                                  /// Sending non-zero `msg.value` for anything but ETH swaps is prohibited
                                                  /// @param recipient Address that will receive swap funds
                                                  /// @param srcToken Source token
                                                  /// @param dstToken Destination token
                                                  /// @param inputAmount Amount of source tokens to swap
                                                  /// @param outputAmount Amount of destination tokens to receive
                                                  /// @param goodUntil Timestamp until the swap will be valid
                                                  /// @param r Clipper order signature (r part)
                                                  /// @param vs Clipper order signature (vs part)
                                                  /// @return returnAmount Amount of destination tokens received
                                                  function clipperSwapTo(
                                                      IClipperExchangeInterface clipperExchange,
                                                      address payable recipient,
                                                      IERC20 srcToken,
                                                      IERC20 dstToken,
                                                      uint256 inputAmount,
                                                      uint256 outputAmount,
                                                      uint256 goodUntil,
                                                      bytes32 r,
                                                      bytes32 vs
                                                  ) public payable returns(uint256 returnAmount) {
                                                      bool srcETH = srcToken == _ETH;
                                                      if (srcETH) {
                                                          if (msg.value != inputAmount) revert RouterErrors.InvalidMsgValue();
                                                      } else if (srcToken == _WETH) {
                                                          srcETH = true;
                                                          if (msg.value != 0) revert RouterErrors.InvalidMsgValue();
                                                          // _WETH.transferFrom(msg.sender, address(this), inputAmount);
                                                          // _WETH.withdraw(inputAmount);
                                                          address weth = address(_WETH);
                                                          bytes4 transferFromSelector = _WETH.transferFrom.selector;
                                                          bytes4 withdrawSelector = _WETH.withdraw.selector;
                                                          /// @solidity memory-safe-assembly
                                                          assembly { // solhint-disable-line no-inline-assembly
                                                              let ptr := mload(0x40)
                                              
                                                              mstore(ptr, transferFromSelector)
                                                              mstore(add(ptr, 0x04), caller())
                                                              mstore(add(ptr, 0x24), address())
                                                              mstore(add(ptr, 0x44), inputAmount)
                                                              if iszero(call(gas(), weth, 0, ptr, 0x64, 0, 0)) {
                                                                  returndatacopy(ptr, 0, returndatasize())
                                                                  revert(ptr, returndatasize())
                                                              }
                                              
                                                              mstore(ptr, withdrawSelector)
                                                              mstore(add(ptr, 0x04), inputAmount)
                                                              if iszero(call(gas(), weth, 0, ptr, 0x24, 0, 0)) {
                                                                  returndatacopy(ptr, 0, returndatasize())
                                                                  revert(ptr, returndatasize())
                                                              }
                                                          }
                                                      } else {
                                                          if (msg.value != 0) revert RouterErrors.InvalidMsgValue();
                                                          srcToken.safeTransferFrom(msg.sender, address(clipperExchange), inputAmount);
                                                      }
                                              
                                                      if (srcETH) {
                                                          // clipperExchange.sellEthForToken{value: inputAmount}(address(dstToken), inputAmount, outputAmount, goodUntil, recipient, signature, _INCH_TAG);
                                                          address clipper = address(clipperExchange);
                                                          bytes4 selector = clipperExchange.sellEthForToken.selector;
                                                          /// @solidity memory-safe-assembly
                                                          assembly { // solhint-disable-line no-inline-assembly
                                                              let ptr := mload(0x40)
                                              
                                                              mstore(ptr, selector)
                                                              mstore(add(ptr, 0x04), dstToken)
                                                              mstore(add(ptr, 0x24), inputAmount)
                                                              mstore(add(ptr, 0x44), outputAmount)
                                                              mstore(add(ptr, 0x64), goodUntil)
                                                              mstore(add(ptr, 0x84), recipient)
                                                              mstore(add(ptr, 0xa4), add(27, shr(_SIGNATURE_V_SHIFT, vs)))
                                                              mstore(add(ptr, 0xc4), r)
                                                              mstore(add(ptr, 0xe4), and(vs, _SIGNATURE_S_MASK))
                                                              mstore(add(ptr, 0x104), 0x120)
                                                              mstore(add(ptr, 0x143), _INCH_TAG_WITH_LENGTH_PREFIX)
                                                              if iszero(call(gas(), clipper, inputAmount, ptr, 0x149, 0, 0)) {
                                                                  returndatacopy(ptr, 0, returndatasize())
                                                                  revert(ptr, returndatasize())
                                                              }
                                                          }
                                                      } else if (dstToken == _ETH || dstToken == _WETH) {
                                                          // clipperExchange.sellTokenForEth(address(srcToken), inputAmount, outputAmount, goodUntil, recipient, signature, _INCH_TAG);
                                                          address clipper = address(clipperExchange);
                                                          bytes4 selector = clipperExchange.sellTokenForEth.selector;
                                                          /// @solidity memory-safe-assembly
                                                          assembly { // solhint-disable-line no-inline-assembly
                                                              let ptr := mload(0x40)
                                              
                                                              mstore(ptr, selector)
                                                              mstore(add(ptr, 0x04), srcToken)
                                                              mstore(add(ptr, 0x24), inputAmount)
                                                              mstore(add(ptr, 0x44), outputAmount)
                                                              mstore(add(ptr, 0x64), goodUntil)
                                                              switch iszero(dstToken)
                                                              case 1 {
                                                                  mstore(add(ptr, 0x84), recipient)
                                                              }
                                                              default {
                                                                  mstore(add(ptr, 0x84), address())
                                                              }
                                                              mstore(add(ptr, 0xa4), add(27, shr(_SIGNATURE_V_SHIFT, vs)))
                                                              mstore(add(ptr, 0xc4), r)
                                                              mstore(add(ptr, 0xe4), and(vs, _SIGNATURE_S_MASK))
                                                              mstore(add(ptr, 0x104), 0x120)
                                                              mstore(add(ptr, 0x143), _INCH_TAG_WITH_LENGTH_PREFIX)
                                                              if iszero(call(gas(), clipper, 0, ptr, 0x149, 0, 0)) {
                                                                  returndatacopy(ptr, 0, returndatasize())
                                                                  revert(ptr, returndatasize())
                                                              }
                                                          }
                                              
                                                          if (dstToken == _WETH) {
                                                              // _WETH.deposit{value: outputAmount}();
                                                              // _WETH.transfer(recipient, outputAmount);
                                                              address weth = address(_WETH);
                                                              bytes4 depositSelector = _WETH.deposit.selector;
                                                              bytes4 transferSelector = _WETH.transfer.selector;
                                                              /// @solidity memory-safe-assembly
                                                              assembly { // solhint-disable-line no-inline-assembly
                                                                  let ptr := mload(0x40)
                                              
                                                                  mstore(ptr, depositSelector)
                                                                  if iszero(call(gas(), weth, outputAmount, ptr, 0x04, 0, 0)) {
                                                                      returndatacopy(ptr, 0, returndatasize())
                                                                      revert(ptr, returndatasize())
                                                                  }
                                              
                                                                  mstore(ptr, transferSelector)
                                                                  mstore(add(ptr, 0x04), recipient)
                                                                  mstore(add(ptr, 0x24), outputAmount)
                                                                  if iszero(call(gas(), weth, 0, ptr, 0x44, 0, 0)) {
                                                                      returndatacopy(ptr, 0, returndatasize())
                                                                      revert(ptr, returndatasize())
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          // clipperExchange.swap(address(srcToken), address(dstToken), inputAmount, outputAmount, goodUntil, recipient, signature, _INCH_TAG);
                                                          address clipper = address(clipperExchange);
                                                          bytes4 selector = clipperExchange.swap.selector;
                                                          /// @solidity memory-safe-assembly
                                                          assembly { // solhint-disable-line no-inline-assembly
                                                              let ptr := mload(0x40)
                                              
                                                              mstore(ptr, selector)
                                                              mstore(add(ptr, 0x04), srcToken)
                                                              mstore(add(ptr, 0x24), dstToken)
                                                              mstore(add(ptr, 0x44), inputAmount)
                                                              mstore(add(ptr, 0x64), outputAmount)
                                                              mstore(add(ptr, 0x84), goodUntil)
                                                              mstore(add(ptr, 0xa4), recipient)
                                                              mstore(add(ptr, 0xc4), add(27, shr(_SIGNATURE_V_SHIFT, vs)))
                                                              mstore(add(ptr, 0xe4), r)
                                                              mstore(add(ptr, 0x104), and(vs, _SIGNATURE_S_MASK))
                                                              mstore(add(ptr, 0x124), 0x140)
                                                              mstore(add(ptr, 0x163), _INCH_TAG_WITH_LENGTH_PREFIX)
                                                              if iszero(call(gas(), clipper, 0, ptr, 0x169, 0, 0)) {
                                                                  returndatacopy(ptr, 0, returndatasize())
                                                                  revert(ptr, returndatasize())
                                                              }
                                                          }
                                                      }
                                              
                                                      return outputAmount;
                                                  }
                                              }
                                              
                                              
                                              // File contracts/interfaces/IAggregationExecutor.sol
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              /// @title Interface for making arbitrary calls during swap
                                              interface IAggregationExecutor {
                                                  /// @notice propagates information about original msg.sender and executes arbitrary data
                                                  function execute(address msgSender) external payable;  // 0x4b64e492
                                              }
                                              
                                              
                                              // File @1inch/solidity-utils/contracts/interfaces/[email protected]
                                              
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              
                                              interface IERC20MetadataUppercase {
                                                  function NAME() external view returns (string memory);  // solhint-disable-line func-name-mixedcase
                                                  function SYMBOL() external view returns (string memory);  // solhint-disable-line func-name-mixedcase
                                              }
                                              
                                              
                                              // File @1inch/solidity-utils/contracts/libraries/[email protected]
                                              
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              /// @title Library with gas-efficient string operations
                                              library StringUtil {
                                                  function toHex(uint256 value) internal pure returns (string memory) {
                                                      return toHex(abi.encodePacked(value));
                                                  }
                                              
                                                  function toHex(address value) internal pure returns (string memory) {
                                                      return toHex(abi.encodePacked(value));
                                                  }
                                              
                                                  function toHex(bytes memory data) internal pure returns (string memory result) {
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          function _toHex16(input) -> output {
                                                              output := or(
                                                                  and(input, 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000),
                                                                  shr(64, and(input, 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000))
                                                              )
                                                              output := or(
                                                                  and(output, 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000),
                                                                  shr(32, and(output, 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000))
                                                              )
                                                              output := or(
                                                                  and(output, 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000),
                                                                  shr(16, and(output, 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000))
                                                              )
                                                              output := or(
                                                                  and(output, 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000),
                                                                  shr(8, and(output, 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000))
                                                              )
                                                              output := or(
                                                                  shr(4, and(output, 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000)),
                                                                  shr(8, and(output, 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00))
                                                              )
                                                              output := add(
                                                                  add(0x3030303030303030303030303030303030303030303030303030303030303030, output),
                                                                  mul(
                                                                      and(
                                                                          shr(4, add(output, 0x0606060606060606060606060606060606060606060606060606060606060606)),
                                                                          0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F
                                                                      ),
                                                                      7   // Change 7 to 39 for lower case output
                                                                  )
                                                              )
                                                          }
                                              
                                                          result := mload(0x40)
                                                          let length := mload(data)
                                                          let resultLength := shl(1, length)
                                                          let toPtr := add(result, 0x22)          // 32 bytes for length + 2 bytes for '0x'
                                                          mstore(0x40, add(toPtr, resultLength))  // move free memory pointer
                                                          mstore(add(result, 2), 0x3078)          // 0x3078 is right aligned so we write to `result + 2`
                                                                                                  // to store the last 2 bytes in the beginning of the string
                                                          mstore(result, add(resultLength, 2))    // extra 2 bytes for '0x'
                                              
                                                          for {
                                                              let fromPtr := add(data, 0x20)
                                                              let endPtr := add(fromPtr, length)
                                                          } lt(fromPtr, endPtr) {
                                                              fromPtr := add(fromPtr, 0x20)
                                                          } {
                                                              let rawData := mload(fromPtr)
                                                              let hexData := _toHex16(rawData)
                                                              mstore(toPtr, hexData)
                                                              toPtr := add(toPtr, 0x20)
                                                              hexData := _toHex16(shl(128, rawData))
                                                              mstore(toPtr, hexData)
                                                              toPtr := add(toPtr, 0x20)
                                                          }
                                                      }
                                                  }
                                              }
                                              
                                              
                                              // File @openzeppelin/contracts/token/ERC20/extensions/[email protected]
                                              
                                              // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              /**
                                               * @dev Interface for the optional metadata functions from the ERC20 standard.
                                               *
                                               * _Available since v4.1._
                                               */
                                              interface IERC20Metadata is IERC20 {
                                                  /**
                                                   * @dev Returns the name of the token.
                                                   */
                                                  function name() external view returns (string memory);
                                              
                                                  /**
                                                   * @dev Returns the symbol of the token.
                                                   */
                                                  function symbol() external view returns (string memory);
                                              
                                                  /**
                                                   * @dev Returns the decimals places of the token.
                                                   */
                                                  function decimals() external view returns (uint8);
                                              }
                                              
                                              
                                              // File @1inch/solidity-utils/contracts/libraries/[email protected]
                                              
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              
                                              
                                              
                                              
                                              library UniERC20 {
                                                  using SafeERC20 for IERC20;
                                              
                                                  error InsufficientBalance();
                                                  error ApproveCalledOnETH();
                                                  error NotEnoughValue();
                                                  error FromIsNotSender();
                                                  error ToIsNotThis();
                                                  error ETHTransferFailed();
                                              
                                                  uint256 private constant _RAW_CALL_GAS_LIMIT = 5000;
                                                  IERC20 private constant _ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
                                                  IERC20 private constant _ZERO_ADDRESS = IERC20(address(0));
                                              
                                                  function isETH(IERC20 token) internal pure returns (bool) {
                                                      return (token == _ZERO_ADDRESS || token == _ETH_ADDRESS);
                                                  }
                                              
                                                  function uniBalanceOf(IERC20 token, address account) internal view returns (uint256) {
                                                      if (isETH(token)) {
                                                          return account.balance;
                                                      } else {
                                                          return token.balanceOf(account);
                                                      }
                                                  }
                                              
                                                  /// @dev note that this function does nothing in case of zero amount
                                                  function uniTransfer(IERC20 token, address payable to, uint256 amount) internal {
                                                      if (amount > 0) {
                                                          if (isETH(token)) {
                                                              if (address(this).balance < amount) revert InsufficientBalance();
                                                              // solhint-disable-next-line avoid-low-level-calls
                                                              (bool success, ) = to.call{value: amount, gas: _RAW_CALL_GAS_LIMIT}("");
                                                              if (!success) revert ETHTransferFailed();
                                                          } else {
                                                              token.safeTransfer(to, amount);
                                                          }
                                                      }
                                                  }
                                              
                                                  /// @dev note that this function does nothing in case of zero amount
                                                  function uniTransferFrom(IERC20 token, address payable from, address to, uint256 amount) internal {
                                                      if (amount > 0) {
                                                          if (isETH(token)) {
                                                              if (msg.value < amount) revert NotEnoughValue();
                                                              if (from != msg.sender) revert FromIsNotSender();
                                                              if (to != address(this)) revert ToIsNotThis();
                                                              if (msg.value > amount) {
                                                                  // Return remainder if exist
                                                                  unchecked {
                                                                      // solhint-disable-next-line avoid-low-level-calls
                                                                      (bool success, ) = from.call{value: msg.value - amount, gas: _RAW_CALL_GAS_LIMIT}("");
                                                                      if (!success) revert ETHTransferFailed();
                                                                  }
                                                              }
                                                          } else {
                                                              token.safeTransferFrom(from, to, amount);
                                                          }
                                                      }
                                                  }
                                              
                                                  function uniSymbol(IERC20 token) internal view returns(string memory) {
                                                      return _uniDecode(token, IERC20Metadata.symbol.selector, IERC20MetadataUppercase.SYMBOL.selector);
                                                  }
                                              
                                                  function uniName(IERC20 token) internal view returns(string memory) {
                                                      return _uniDecode(token, IERC20Metadata.name.selector, IERC20MetadataUppercase.NAME.selector);
                                                  }
                                              
                                                  function uniApprove(IERC20 token, address to, uint256 amount) internal {
                                                      if (isETH(token)) revert ApproveCalledOnETH();
                                              
                                                      token.forceApprove(to, amount);
                                                  }
                                              
                                                  /// 20K gas is provided to account for possible implementations of name/symbol
                                                  /// (token implementation might be behind proxy or store the value in storage)
                                                  function _uniDecode(IERC20 token, bytes4 lowerCaseSelector, bytes4 upperCaseSelector) private view returns(string memory result) {
                                                      if (isETH(token)) {
                                                          return "ETH";
                                                      }
                                              
                                                      (bool success, bytes memory data) = address(token).staticcall{ gas: 20000 }(
                                                          abi.encodeWithSelector(lowerCaseSelector)
                                                      );
                                                      if (!success) {
                                                          (success, data) = address(token).staticcall{ gas: 20000 }(
                                                              abi.encodeWithSelector(upperCaseSelector)
                                                          );
                                                      }
                                              
                                                      if (success && data.length >= 0x40) {
                                                          (uint256 offset, uint256 len) = abi.decode(data, (uint256, uint256));
                                                          if (offset == 0x20 && len > 0 && data.length == 0x40 + len) {
                                                              /// @solidity memory-safe-assembly
                                                              assembly { // solhint-disable-line no-inline-assembly
                                                                  result := add(data, 0x20)
                                                              }
                                                              return result;
                                                          }
                                                      }
                                              
                                                      if (success && data.length == 32) {
                                                          uint256 len = 0;
                                                          while (len < data.length && data[len] >= 0x20 && data[len] <= 0x7E) {
                                                              unchecked {
                                                                  len++;
                                                              }
                                                          }
                                              
                                                          if (len > 0) {
                                                              /// @solidity memory-safe-assembly
                                                              assembly { // solhint-disable-line no-inline-assembly
                                                                  mstore(data, len)
                                                              }
                                                              return string(data);
                                                          }
                                                      }
                                              
                                                      return StringUtil.toHex(address(token));
                                                  }
                                              }
                                              
                                              
                                              // File contracts/routers/GenericRouter.sol
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              
                                              
                                              
                                              
                                              contract GenericRouter is EthReceiver {
                                                  using UniERC20 for IERC20;
                                                  using SafeERC20 for IERC20;
                                              
                                                  error ZeroMinReturn();
                                                  error ZeroReturnAmount();
                                              
                                                  uint256 private constant _PARTIAL_FILL = 1 << 0;
                                                  uint256 private constant _REQUIRES_EXTRA_ETH = 1 << 1;
                                              
                                                  struct SwapDescription {
                                                      IERC20 srcToken;
                                                      IERC20 dstToken;
                                                      address payable srcReceiver;
                                                      address payable dstReceiver;
                                                      uint256 amount;
                                                      uint256 minReturnAmount;
                                                      uint256 flags;
                                                  }
                                              
                                                  /// @notice Performs a swap, delegating all calls encoded in `data` to `executor`. See tests for usage examples
                                                  /// @dev router keeps 1 wei of every token on the contract balance for gas optimisations reasons. This affects first swap of every token by leaving 1 wei on the contract.
                                                  /// @param executor Aggregation executor that executes calls described in `data`
                                                  /// @param desc Swap description
                                                  /// @param permit Should contain valid permit that can be used in `IERC20Permit.permit` calls.
                                                  /// @param data Encoded calls that `caller` should execute in between of swaps
                                                  /// @return returnAmount Resulting token amount
                                                  /// @return spentAmount Source token amount
                                                  function swap(
                                                      IAggregationExecutor executor,
                                                      SwapDescription calldata desc,
                                                      bytes calldata permit,
                                                      bytes calldata data
                                                  )
                                                      external
                                                      payable
                                                      returns (
                                                          uint256 returnAmount,
                                                          uint256 spentAmount
                                                      )
                                                  {
                                                      if (desc.minReturnAmount == 0) revert ZeroMinReturn();
                                              
                                                      IERC20 srcToken = desc.srcToken;
                                                      IERC20 dstToken = desc.dstToken;
                                              
                                                      bool srcETH = srcToken.isETH();
                                                      if (desc.flags & _REQUIRES_EXTRA_ETH != 0) {
                                                          if (msg.value <= (srcETH ? desc.amount : 0)) revert RouterErrors.InvalidMsgValue();
                                                      } else {
                                                          if (msg.value != (srcETH ? desc.amount : 0)) revert RouterErrors.InvalidMsgValue();
                                                      }
                                              
                                                      if (!srcETH) {
                                                          if (permit.length > 0) {
                                                              srcToken.safePermit(permit);
                                                          }
                                                          srcToken.safeTransferFrom(msg.sender, desc.srcReceiver, desc.amount);
                                                      }
                                              
                                                      _execute(executor, msg.sender, desc.amount, data);
                                              
                                                      spentAmount = desc.amount;
                                                      // we leave 1 wei on the router for gas optimisations reasons
                                                      returnAmount = dstToken.uniBalanceOf(address(this));
                                                      if (returnAmount == 0) revert ZeroReturnAmount();
                                                      unchecked { returnAmount--; }
                                              
                                                      if (desc.flags & _PARTIAL_FILL != 0) {
                                                          uint256 unspentAmount = srcToken.uniBalanceOf(address(this));
                                                          if (unspentAmount > 1) {
                                                              // we leave 1 wei on the router for gas optimisations reasons
                                                              unchecked { unspentAmount--; }
                                                              spentAmount -= unspentAmount;
                                                              srcToken.uniTransfer(payable(msg.sender), unspentAmount);
                                                          }
                                                          if (returnAmount * desc.amount < desc.minReturnAmount * spentAmount) revert RouterErrors.ReturnAmountIsNotEnough();
                                                      } else {
                                                          if (returnAmount < desc.minReturnAmount) revert RouterErrors.ReturnAmountIsNotEnough();
                                                      }
                                              
                                                      address payable dstReceiver = (desc.dstReceiver == address(0)) ? payable(msg.sender) : desc.dstReceiver;
                                                      dstToken.uniTransfer(dstReceiver, returnAmount);
                                                  }
                                              
                                                  function _execute(
                                                      IAggregationExecutor executor,
                                                      address srcTokenOwner,
                                                      uint256 inputAmount,
                                                      bytes calldata data
                                                  ) private {
                                                      bytes4 executeSelector = executor.execute.selector;
                                                      /// @solidity memory-safe-assembly
                                                      assembly {  // solhint-disable-line no-inline-assembly
                                                          let ptr := mload(0x40)
                                              
                                                          mstore(ptr, executeSelector)
                                                          mstore(add(ptr, 0x04), srcTokenOwner)
                                                          calldatacopy(add(ptr, 0x24), data.offset, data.length)
                                                          mstore(add(add(ptr, 0x24), data.length), inputAmount)
                                              
                                                          if iszero(call(gas(), executor, callvalue(), ptr, add(0x44, data.length), 0, 0)) {
                                                              returndatacopy(ptr, 0, returndatasize())
                                                              revert(ptr, returndatasize())
                                                          }
                                                      }
                                                  }
                                              }
                                              
                                              
                                              // File contracts/routers/UnoswapRouter.sol
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              
                                              
                                              
                                              contract UnoswapRouter is EthReceiver {
                                                  using SafeERC20 for IERC20;
                                              
                                                  error ReservesCallFailed();
                                                  error SwapAmountTooLarge();
                                              
                                                  bytes4 private constant _TRANSFER_FROM_CALL_SELECTOR = 0x23b872dd;
                                                  bytes4 private constant _WETH_DEPOSIT_CALL_SELECTOR = 0xd0e30db0;
                                                  bytes4 private constant _WETH_WITHDRAW_CALL_SELECTOR = 0x2e1a7d4d;
                                                  bytes4 private constant _ERC20_TRANSFER_CALL_SELECTOR = 0xa9059cbb;
                                                  uint256 private constant _ADDRESS_MASK =   0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
                                                  uint256 private constant _REVERSE_MASK =   0x8000000000000000000000000000000000000000000000000000000000000000;
                                                  uint256 private constant _WETH_MASK =      0x4000000000000000000000000000000000000000000000000000000000000000;
                                                  uint256 private constant _NUMERATOR_MASK = 0x0000000000000000ffffffff0000000000000000000000000000000000000000;
                                                  /// @dev WETH address is network-specific and needs to be changed before deployment.
                                                  /// It can not be moved to immutable as immutables are not supported in assembly
                                                  address private constant _WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
                                                  bytes4 private constant _UNISWAP_PAIR_RESERVES_CALL_SELECTOR = 0x0902f1ac;
                                                  bytes4 private constant _UNISWAP_PAIR_SWAP_CALL_SELECTOR = 0x022c0d9f;
                                                  uint256 private constant _DENOMINATOR = 1e9;
                                                  uint256 private constant _NUMERATOR_OFFSET = 160;
                                                  uint256 private constant _MAX_SWAP_AMOUNT = (1 << 112) - 1;  // type(uint112).max;
                                              
                                                  /// @notice Same as `unoswapTo` but calls permit first,
                                                  /// allowing to approve token spending and make a swap in one transaction.
                                                  /// @param recipient Address that will receive swapped funds
                                                  /// @param srcToken Source token
                                                  /// @param amount Amount of source tokens to swap
                                                  /// @param minReturn Minimal allowed returnAmount to make transaction commit
                                                  /// @param pools Pools chain used for swaps. Pools src and dst tokens should match to make swap happen
                                                  /// @param permit Should contain valid permit that can be used in `IERC20Permit.permit` calls.
                                                  /// See tests for examples
                                                  function unoswapToWithPermit(
                                                      address payable recipient,
                                                      IERC20 srcToken,
                                                      uint256 amount,
                                                      uint256 minReturn,
                                                      uint256[] calldata pools,
                                                      bytes calldata permit
                                                  ) external returns(uint256 returnAmount) {
                                                      srcToken.safePermit(permit);
                                                      return _unoswap(recipient, srcToken, amount, minReturn, pools);
                                                  }
                                              
                                                  /// @notice Performs swap using Uniswap exchange. Wraps and unwraps ETH if required.
                                                  /// Sending non-zero `msg.value` for anything but ETH swaps is prohibited
                                                  /// @param recipient Address that will receive swapped funds
                                                  /// @param srcToken Source token
                                                  /// @param amount Amount of source tokens to swap
                                                  /// @param minReturn Minimal allowed returnAmount to make transaction commit
                                                  /// @param pools Pools chain used for swaps. Pools src and dst tokens should match to make swap happen
                                                  function unoswapTo(
                                                      address payable recipient,
                                                      IERC20 srcToken,
                                                      uint256 amount,
                                                      uint256 minReturn,
                                                      uint256[] calldata pools
                                                  ) external payable returns(uint256 returnAmount) {
                                                      return _unoswap(recipient, srcToken, amount, minReturn, pools);
                                                  }
                                              
                                                  /// @notice Performs swap using Uniswap exchange. Wraps and unwraps ETH if required.
                                                  /// Sending non-zero `msg.value` for anything but ETH swaps is prohibited
                                                  /// @param srcToken Source token
                                                  /// @param amount Amount of source tokens to swap
                                                  /// @param minReturn Minimal allowed returnAmount to make transaction commit
                                                  /// @param pools Pools chain used for swaps. Pools src and dst tokens should match to make swap happen
                                                  function unoswap(
                                                      IERC20 srcToken,
                                                      uint256 amount,
                                                      uint256 minReturn,
                                                      uint256[] calldata pools
                                                  ) external payable returns(uint256 returnAmount) {
                                                      return _unoswap(payable(msg.sender), srcToken, amount, minReturn, pools);
                                                  }
                                              
                                                  function _unoswap(
                                                      address payable recipient,
                                                      IERC20 srcToken,
                                                      uint256 amount,
                                                      uint256 minReturn,
                                                      uint256[] calldata pools
                                                  ) private returns(uint256 returnAmount) {
                                                      assembly {  // solhint-disable-line no-inline-assembly
                                                          function reRevert() {
                                                              returndatacopy(0, 0, returndatasize())
                                                              revert(0, returndatasize())
                                                          }
                                              
                                                          function validateERC20Transfer(status) {
                                                              if iszero(status) {
                                                                  reRevert()
                                                              }
                                                              let success := or(
                                                                  iszero(returndatasize()),                       // empty return data
                                                                  and(gt(returndatasize(), 31), eq(mload(0), 1))  // true in return data
                                                              )
                                                              if iszero(success) {
                                                                  mstore(0, 0xf27f64e400000000000000000000000000000000000000000000000000000000)  // ERC20TransferFailed()
                                                                  revert(0, 4)
                                                              }
                                                          }
                                              
                                                          function swap(emptyPtr, swapAmount, pair, reversed, numerator, to) -> ret {
                                                              mstore(emptyPtr, _UNISWAP_PAIR_RESERVES_CALL_SELECTOR)
                                                              if iszero(staticcall(gas(), pair, emptyPtr, 0x4, emptyPtr, 0x40)) {
                                                                  reRevert()
                                                              }
                                                              if iszero(eq(returndatasize(), 0x60)) {
                                                                  mstore(0, 0x85cd58dc00000000000000000000000000000000000000000000000000000000)  // ReservesCallFailed()
                                                                  revert(0, 4)
                                                              }
                                              
                                                              let reserve0 := mload(emptyPtr)
                                                              let reserve1 := mload(add(emptyPtr, 0x20))
                                                              if reversed {
                                                                  let tmp := reserve0
                                                                  reserve0 := reserve1
                                                                  reserve1 := tmp
                                                              }
                                                              // this will not overflow as reserve0, reserve1 and ret fit to 112 bit and numerator and _DENOMINATOR fit to 32 bit
                                                              ret := mul(swapAmount, numerator)
                                                              ret := div(mul(ret, reserve1), add(ret, mul(reserve0, _DENOMINATOR)))
                                              
                                                              mstore(emptyPtr, _UNISWAP_PAIR_SWAP_CALL_SELECTOR)
                                                              reversed := iszero(reversed)
                                                              mstore(add(emptyPtr, 0x04), mul(ret, iszero(reversed)))
                                                              mstore(add(emptyPtr, 0x24), mul(ret, reversed))
                                                              mstore(add(emptyPtr, 0x44), to)
                                                              mstore(add(emptyPtr, 0x64), 0x80)
                                                              mstore(add(emptyPtr, 0x84), 0)
                                                              if iszero(call(gas(), pair, 0, emptyPtr, 0xa4, 0, 0)) {
                                                                  reRevert()
                                                              }
                                                          }
                                              
                                                          // make sure that input amount fits in 112 bit
                                                          if gt(amount, _MAX_SWAP_AMOUNT) {
                                                              mstore(0, 0xcf0b4d3a00000000000000000000000000000000000000000000000000000000)  // SwapAmountTooLarge()
                                                              revert(0, 4)
                                                          }
                                              
                                                          let emptyPtr := mload(0x40)
                                                          mstore(0x40, add(emptyPtr, 0xc0))
                                              
                                                          let poolsEndOffset := add(pools.offset, shl(5, pools.length))
                                                          let rawPair := calldataload(pools.offset)
                                                          switch srcToken
                                                          case 0 {
                                                              if iszero(eq(amount, callvalue())) {
                                                                  mstore(0, 0x1841b4e100000000000000000000000000000000000000000000000000000000)  // InvalidMsgValue()
                                                                  revert(0, 4)
                                                              }
                                              
                                                              mstore(emptyPtr, _WETH_DEPOSIT_CALL_SELECTOR)
                                                              if iszero(call(gas(), _WETH, amount, emptyPtr, 0x4, 0, 0)) {
                                                                  reRevert()
                                                              }
                                              
                                                              mstore(emptyPtr, _ERC20_TRANSFER_CALL_SELECTOR)
                                                              mstore(add(emptyPtr, 0x4), and(rawPair, _ADDRESS_MASK))
                                                              mstore(add(emptyPtr, 0x24), amount)
                                                              if iszero(call(gas(), _WETH, 0, emptyPtr, 0x44, 0, 0)) {
                                                                  reRevert()
                                                              }
                                                          }
                                                          default {
                                                              if callvalue() {
                                                                  mstore(0, 0x1841b4e100000000000000000000000000000000000000000000000000000000)  // InvalidMsgValue()
                                                                  revert(0, 4)
                                                              }
                                              
                                                              mstore(emptyPtr, _TRANSFER_FROM_CALL_SELECTOR)
                                                              mstore(add(emptyPtr, 0x4), caller())
                                                              mstore(add(emptyPtr, 0x24), and(rawPair, _ADDRESS_MASK))
                                                              mstore(add(emptyPtr, 0x44), amount)
                                                              validateERC20Transfer(
                                                                  call(gas(), srcToken, 0, emptyPtr, 0x64, 0, 0x20)
                                                              )
                                                          }
                                              
                                                          returnAmount := amount
                                              
                                                          for {let i := add(pools.offset, 0x20)} lt(i, poolsEndOffset) {i := add(i, 0x20)} {
                                                              let nextRawPair := calldataload(i)
                                              
                                                              returnAmount := swap(
                                                                  emptyPtr,
                                                                  returnAmount,
                                                                  and(rawPair, _ADDRESS_MASK),
                                                                  and(rawPair, _REVERSE_MASK),
                                                                  shr(_NUMERATOR_OFFSET, and(rawPair, _NUMERATOR_MASK)),
                                                                  and(nextRawPair, _ADDRESS_MASK)
                                                              )
                                              
                                                              rawPair := nextRawPair
                                                          }
                                              
                                                          switch and(rawPair, _WETH_MASK)
                                                          case 0 {
                                                              returnAmount := swap(
                                                                  emptyPtr,
                                                                  returnAmount,
                                                                  and(rawPair, _ADDRESS_MASK),
                                                                  and(rawPair, _REVERSE_MASK),
                                                                  shr(_NUMERATOR_OFFSET, and(rawPair, _NUMERATOR_MASK)),
                                                                  recipient
                                                              )
                                                          }
                                                          default {
                                                              returnAmount := swap(
                                                                  emptyPtr,
                                                                  returnAmount,
                                                                  and(rawPair, _ADDRESS_MASK),
                                                                  and(rawPair, _REVERSE_MASK),
                                                                  shr(_NUMERATOR_OFFSET, and(rawPair, _NUMERATOR_MASK)),
                                                                  address()
                                                              )
                                              
                                                              mstore(emptyPtr, _WETH_WITHDRAW_CALL_SELECTOR)
                                                              mstore(add(emptyPtr, 0x04), returnAmount)
                                                              if iszero(call(gas(), _WETH, 0, emptyPtr, 0x24, 0, 0)) {
                                                                  reRevert()
                                                              }
                                              
                                                              if iszero(call(gas(), recipient, returnAmount, 0, 0, 0, 0)) {
                                                                  reRevert()
                                                              }
                                                          }
                                                      }
                                                      if (returnAmount < minReturn) revert RouterErrors.ReturnAmountIsNotEnough();
                                                  }
                                              }
                                              
                                              
                                              // File contracts/interfaces/IUniswapV3Pool.sol
                                              
                                              pragma solidity 0.8.17;
                                              
                                              interface IUniswapV3Pool {
                                                  /// @notice Swap token0 for token1, or token1 for token0
                                                  /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback
                                                  /// @param recipient The address to receive the output of the swap
                                                  /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
                                                  /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
                                                  /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
                                                  /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
                                                  /// @param data Any data to be passed through to the callback
                                                  /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
                                                  /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
                                                  function swap(
                                                      address recipient,
                                                      bool zeroForOne,
                                                      int256 amountSpecified,
                                                      uint160 sqrtPriceLimitX96,
                                                      bytes calldata data
                                                  ) external returns (int256 amount0, int256 amount1);
                                              
                                                  /// @notice The first of the two tokens of the pool, sorted by address
                                                  /// @return The token contract address
                                                  function token0() external view returns (address);
                                              
                                                  /// @notice The second of the two tokens of the pool, sorted by address
                                                  /// @return The token contract address
                                                  function token1() external view returns (address);
                                              
                                                  /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
                                                  /// @return The fee
                                                  function fee() external view returns (uint24);
                                              }
                                              
                                              
                                              // File contracts/interfaces/IUniswapV3SwapCallback.sol
                                              
                                              pragma solidity 0.8.17;
                                              
                                              /// @title Callback for IUniswapV3PoolActions#swap
                                              /// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
                                              interface IUniswapV3SwapCallback {
                                                  /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
                                                  /// @dev In the implementation you must pay the pool tokens owed for the swap.
                                                  /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
                                                  /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
                                                  /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
                                                  /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
                                                  /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
                                                  /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
                                                  /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
                                                  function uniswapV3SwapCallback(
                                                      int256 amount0Delta,
                                                      int256 amount1Delta,
                                                      bytes calldata data
                                                  ) external;
                                              }
                                              
                                              
                                              // File @openzeppelin/contracts/utils/[email protected]
                                              
                                              // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
                                              
                                              pragma solidity ^0.8.1;
                                              
                                              /**
                                               * @dev Collection of functions related to the address type
                                               */
                                              library Address {
                                                  /**
                                                   * @dev Returns true if `account` is a contract.
                                                   *
                                                   * [IMPORTANT]
                                                   * ====
                                                   * It is unsafe to assume that an address for which this function returns
                                                   * false is an externally-owned account (EOA) and not a contract.
                                                   *
                                                   * Among others, `isContract` will return false for the following
                                                   * types of addresses:
                                                   *
                                                   *  - an externally-owned account
                                                   *  - a contract in construction
                                                   *  - an address where a contract will be created
                                                   *  - an address where a contract lived, but was destroyed
                                                   * ====
                                                   *
                                                   * [IMPORTANT]
                                                   * ====
                                                   * You shouldn't rely on `isContract` to protect against flash loan attacks!
                                                   *
                                                   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                                                   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                                                   * constructor.
                                                   * ====
                                                   */
                                                  function isContract(address account) internal view returns (bool) {
                                                      // This method relies on extcodesize/address.code.length, which returns 0
                                                      // for contracts in construction, since the code is only stored at the end
                                                      // of the constructor execution.
                                              
                                                      return account.code.length > 0;
                                                  }
                                              
                                                  /**
                                                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                                                   * `recipient`, forwarding all available gas and reverting on errors.
                                                   *
                                                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                                                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                                                   * imposed by `transfer`, making them unable to receive funds via
                                                   * `transfer`. {sendValue} removes this limitation.
                                                   *
                                                   * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                                                   *
                                                   * IMPORTANT: because control is transferred to `recipient`, care must be
                                                   * taken to not create reentrancy vulnerabilities. Consider using
                                                   * {ReentrancyGuard} or the
                                                   * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                                                   */
                                                  function sendValue(address payable recipient, uint256 amount) internal {
                                                      require(address(this).balance >= amount, "Address: insufficient balance");
                                              
                                                      (bool success, ) = recipient.call{value: amount}("");
                                                      require(success, "Address: unable to send value, recipient may have reverted");
                                                  }
                                              
                                                  /**
                                                   * @dev Performs a Solidity function call using a low level `call`. A
                                                   * plain `call` is an unsafe replacement for a function call: use this
                                                   * function instead.
                                                   *
                                                   * If `target` reverts with a revert reason, it is bubbled up by this
                                                   * function (like regular Solidity function calls).
                                                   *
                                                   * Returns the raw returned data. To convert to the expected return value,
                                                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `target` must be a contract.
                                                   * - calling `target` with `data` must not revert.
                                                   *
                                                   * _Available since v3.1._
                                                   */
                                                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                                      return functionCall(target, data, "Address: low-level call failed");
                                                  }
                                              
                                                  /**
                                                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                                                   * `errorMessage` as a fallback revert reason when `target` reverts.
                                                   *
                                                   * _Available since v3.1._
                                                   */
                                                  function functionCall(
                                                      address target,
                                                      bytes memory data,
                                                      string memory errorMessage
                                                  ) internal returns (bytes memory) {
                                                      return functionCallWithValue(target, data, 0, errorMessage);
                                                  }
                                              
                                                  /**
                                                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                                   * but also transferring `value` wei to `target`.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - the calling contract must have an ETH balance of at least `value`.
                                                   * - the called Solidity function must be `payable`.
                                                   *
                                                   * _Available since v3.1._
                                                   */
                                                  function functionCallWithValue(
                                                      address target,
                                                      bytes memory data,
                                                      uint256 value
                                                  ) internal returns (bytes memory) {
                                                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                                                  }
                                              
                                                  /**
                                                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                                                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                                                   *
                                                   * _Available since v3.1._
                                                   */
                                                  function functionCallWithValue(
                                                      address target,
                                                      bytes memory data,
                                                      uint256 value,
                                                      string memory errorMessage
                                                  ) internal returns (bytes memory) {
                                                      require(address(this).balance >= value, "Address: insufficient balance for call");
                                                      require(isContract(target), "Address: call to non-contract");
                                              
                                                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                                                      return verifyCallResult(success, returndata, errorMessage);
                                                  }
                                              
                                                  /**
                                                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                                   * but performing a static call.
                                                   *
                                                   * _Available since v3.3._
                                                   */
                                                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                                      return functionStaticCall(target, data, "Address: low-level static call failed");
                                                  }
                                              
                                                  /**
                                                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                                                   * but performing a static call.
                                                   *
                                                   * _Available since v3.3._
                                                   */
                                                  function functionStaticCall(
                                                      address target,
                                                      bytes memory data,
                                                      string memory errorMessage
                                                  ) internal view returns (bytes memory) {
                                                      require(isContract(target), "Address: static call to non-contract");
                                              
                                                      (bool success, bytes memory returndata) = target.staticcall(data);
                                                      return verifyCallResult(success, returndata, errorMessage);
                                                  }
                                              
                                                  /**
                                                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                                   * but performing a delegate call.
                                                   *
                                                   * _Available since v3.4._
                                                   */
                                                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                                                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                                                  }
                                              
                                                  /**
                                                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                                                   * but performing a delegate call.
                                                   *
                                                   * _Available since v3.4._
                                                   */
                                                  function functionDelegateCall(
                                                      address target,
                                                      bytes memory data,
                                                      string memory errorMessage
                                                  ) internal returns (bytes memory) {
                                                      require(isContract(target), "Address: delegate call to non-contract");
                                              
                                                      (bool success, bytes memory returndata) = target.delegatecall(data);
                                                      return verifyCallResult(success, returndata, errorMessage);
                                                  }
                                              
                                                  /**
                                                   * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                                                   * revert reason using the provided one.
                                                   *
                                                   * _Available since v4.3._
                                                   */
                                                  function verifyCallResult(
                                                      bool success,
                                                      bytes memory returndata,
                                                      string memory errorMessage
                                                  ) internal pure returns (bytes memory) {
                                                      if (success) {
                                                          return returndata;
                                                      } else {
                                                          // Look for revert reason and bubble it up if present
                                                          if (returndata.length > 0) {
                                                              // The easiest way to bubble the revert reason is using memory via assembly
                                                              /// @solidity memory-safe-assembly
                                                              assembly {
                                                                  let returndata_size := mload(returndata)
                                                                  revert(add(32, returndata), returndata_size)
                                                              }
                                                          } else {
                                                              revert(errorMessage);
                                                          }
                                                      }
                                                  }
                                              }
                                              
                                              
                                              // File @openzeppelin/contracts/utils/math/[email protected]
                                              
                                              // OpenZeppelin Contracts (last updated v4.7.0) (utils/math/SafeCast.sol)
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              /**
                                               * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
                                               * checks.
                                               *
                                               * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
                                               * easily result in undesired exploitation or bugs, since developers usually
                                               * assume that overflows raise errors. `SafeCast` restores this intuition by
                                               * reverting the transaction when such an operation overflows.
                                               *
                                               * Using this library instead of the unchecked operations eliminates an entire
                                               * class of bugs, so it's recommended to use it always.
                                               *
                                               * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
                                               * all math on `uint256` and `int256` and then downcasting.
                                               */
                                              library SafeCast {
                                                  /**
                                                   * @dev Returns the downcasted uint248 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint248).
                                                   *
                                                   * Counterpart to Solidity's `uint248` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 248 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint248(uint256 value) internal pure returns (uint248) {
                                                      require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
                                                      return uint248(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint240 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint240).
                                                   *
                                                   * Counterpart to Solidity's `uint240` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 240 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint240(uint256 value) internal pure returns (uint240) {
                                                      require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
                                                      return uint240(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint232 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint232).
                                                   *
                                                   * Counterpart to Solidity's `uint232` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 232 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint232(uint256 value) internal pure returns (uint232) {
                                                      require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
                                                      return uint232(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint224 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint224).
                                                   *
                                                   * Counterpart to Solidity's `uint224` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 224 bits
                                                   *
                                                   * _Available since v4.2._
                                                   */
                                                  function toUint224(uint256 value) internal pure returns (uint224) {
                                                      require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                                                      return uint224(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint216 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint216).
                                                   *
                                                   * Counterpart to Solidity's `uint216` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 216 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint216(uint256 value) internal pure returns (uint216) {
                                                      require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
                                                      return uint216(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint208 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint208).
                                                   *
                                                   * Counterpart to Solidity's `uint208` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 208 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint208(uint256 value) internal pure returns (uint208) {
                                                      require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
                                                      return uint208(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint200 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint200).
                                                   *
                                                   * Counterpart to Solidity's `uint200` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 200 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint200(uint256 value) internal pure returns (uint200) {
                                                      require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
                                                      return uint200(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint192 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint192).
                                                   *
                                                   * Counterpart to Solidity's `uint192` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 192 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint192(uint256 value) internal pure returns (uint192) {
                                                      require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
                                                      return uint192(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint184 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint184).
                                                   *
                                                   * Counterpart to Solidity's `uint184` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 184 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint184(uint256 value) internal pure returns (uint184) {
                                                      require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
                                                      return uint184(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint176 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint176).
                                                   *
                                                   * Counterpart to Solidity's `uint176` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 176 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint176(uint256 value) internal pure returns (uint176) {
                                                      require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
                                                      return uint176(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint168 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint168).
                                                   *
                                                   * Counterpart to Solidity's `uint168` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 168 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint168(uint256 value) internal pure returns (uint168) {
                                                      require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
                                                      return uint168(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint160 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint160).
                                                   *
                                                   * Counterpart to Solidity's `uint160` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 160 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint160(uint256 value) internal pure returns (uint160) {
                                                      require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
                                                      return uint160(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint152 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint152).
                                                   *
                                                   * Counterpart to Solidity's `uint152` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 152 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint152(uint256 value) internal pure returns (uint152) {
                                                      require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
                                                      return uint152(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint144 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint144).
                                                   *
                                                   * Counterpart to Solidity's `uint144` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 144 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint144(uint256 value) internal pure returns (uint144) {
                                                      require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
                                                      return uint144(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint136 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint136).
                                                   *
                                                   * Counterpart to Solidity's `uint136` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 136 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint136(uint256 value) internal pure returns (uint136) {
                                                      require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
                                                      return uint136(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint128 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint128).
                                                   *
                                                   * Counterpart to Solidity's `uint128` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 128 bits
                                                   *
                                                   * _Available since v2.5._
                                                   */
                                                  function toUint128(uint256 value) internal pure returns (uint128) {
                                                      require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                                                      return uint128(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint120 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint120).
                                                   *
                                                   * Counterpart to Solidity's `uint120` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 120 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint120(uint256 value) internal pure returns (uint120) {
                                                      require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
                                                      return uint120(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint112 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint112).
                                                   *
                                                   * Counterpart to Solidity's `uint112` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 112 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint112(uint256 value) internal pure returns (uint112) {
                                                      require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
                                                      return uint112(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint104 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint104).
                                                   *
                                                   * Counterpart to Solidity's `uint104` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 104 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint104(uint256 value) internal pure returns (uint104) {
                                                      require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
                                                      return uint104(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint96 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint96).
                                                   *
                                                   * Counterpart to Solidity's `uint96` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 96 bits
                                                   *
                                                   * _Available since v4.2._
                                                   */
                                                  function toUint96(uint256 value) internal pure returns (uint96) {
                                                      require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                                                      return uint96(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint88 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint88).
                                                   *
                                                   * Counterpart to Solidity's `uint88` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 88 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint88(uint256 value) internal pure returns (uint88) {
                                                      require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
                                                      return uint88(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint80 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint80).
                                                   *
                                                   * Counterpart to Solidity's `uint80` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 80 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint80(uint256 value) internal pure returns (uint80) {
                                                      require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
                                                      return uint80(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint72 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint72).
                                                   *
                                                   * Counterpart to Solidity's `uint72` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 72 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint72(uint256 value) internal pure returns (uint72) {
                                                      require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
                                                      return uint72(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint64 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint64).
                                                   *
                                                   * Counterpart to Solidity's `uint64` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 64 bits
                                                   *
                                                   * _Available since v2.5._
                                                   */
                                                  function toUint64(uint256 value) internal pure returns (uint64) {
                                                      require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                                                      return uint64(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint56 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint56).
                                                   *
                                                   * Counterpart to Solidity's `uint56` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 56 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint56(uint256 value) internal pure returns (uint56) {
                                                      require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
                                                      return uint56(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint48 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint48).
                                                   *
                                                   * Counterpart to Solidity's `uint48` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 48 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint48(uint256 value) internal pure returns (uint48) {
                                                      require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
                                                      return uint48(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint40 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint40).
                                                   *
                                                   * Counterpart to Solidity's `uint40` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 40 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint40(uint256 value) internal pure returns (uint40) {
                                                      require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
                                                      return uint40(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint32 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint32).
                                                   *
                                                   * Counterpart to Solidity's `uint32` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 32 bits
                                                   *
                                                   * _Available since v2.5._
                                                   */
                                                  function toUint32(uint256 value) internal pure returns (uint32) {
                                                      require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                                                      return uint32(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint24 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint24).
                                                   *
                                                   * Counterpart to Solidity's `uint24` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 24 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toUint24(uint256 value) internal pure returns (uint24) {
                                                      require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
                                                      return uint24(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint16 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint16).
                                                   *
                                                   * Counterpart to Solidity's `uint16` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 16 bits
                                                   *
                                                   * _Available since v2.5._
                                                   */
                                                  function toUint16(uint256 value) internal pure returns (uint16) {
                                                      require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                                                      return uint16(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted uint8 from uint256, reverting on
                                                   * overflow (when the input is greater than largest uint8).
                                                   *
                                                   * Counterpart to Solidity's `uint8` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 8 bits
                                                   *
                                                   * _Available since v2.5._
                                                   */
                                                  function toUint8(uint256 value) internal pure returns (uint8) {
                                                      require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                                                      return uint8(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Converts a signed int256 into an unsigned uint256.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must be greater than or equal to 0.
                                                   *
                                                   * _Available since v3.0._
                                                   */
                                                  function toUint256(int256 value) internal pure returns (uint256) {
                                                      require(value >= 0, "SafeCast: value must be positive");
                                                      return uint256(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int248 from int256, reverting on
                                                   * overflow (when the input is less than smallest int248 or
                                                   * greater than largest int248).
                                                   *
                                                   * Counterpart to Solidity's `int248` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 248 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt248(int256 value) internal pure returns (int248) {
                                                      require(value >= type(int248).min && value <= type(int248).max, "SafeCast: value doesn't fit in 248 bits");
                                                      return int248(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int240 from int256, reverting on
                                                   * overflow (when the input is less than smallest int240 or
                                                   * greater than largest int240).
                                                   *
                                                   * Counterpart to Solidity's `int240` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 240 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt240(int256 value) internal pure returns (int240) {
                                                      require(value >= type(int240).min && value <= type(int240).max, "SafeCast: value doesn't fit in 240 bits");
                                                      return int240(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int232 from int256, reverting on
                                                   * overflow (when the input is less than smallest int232 or
                                                   * greater than largest int232).
                                                   *
                                                   * Counterpart to Solidity's `int232` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 232 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt232(int256 value) internal pure returns (int232) {
                                                      require(value >= type(int232).min && value <= type(int232).max, "SafeCast: value doesn't fit in 232 bits");
                                                      return int232(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int224 from int256, reverting on
                                                   * overflow (when the input is less than smallest int224 or
                                                   * greater than largest int224).
                                                   *
                                                   * Counterpart to Solidity's `int224` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 224 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt224(int256 value) internal pure returns (int224) {
                                                      require(value >= type(int224).min && value <= type(int224).max, "SafeCast: value doesn't fit in 224 bits");
                                                      return int224(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int216 from int256, reverting on
                                                   * overflow (when the input is less than smallest int216 or
                                                   * greater than largest int216).
                                                   *
                                                   * Counterpart to Solidity's `int216` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 216 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt216(int256 value) internal pure returns (int216) {
                                                      require(value >= type(int216).min && value <= type(int216).max, "SafeCast: value doesn't fit in 216 bits");
                                                      return int216(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int208 from int256, reverting on
                                                   * overflow (when the input is less than smallest int208 or
                                                   * greater than largest int208).
                                                   *
                                                   * Counterpart to Solidity's `int208` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 208 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt208(int256 value) internal pure returns (int208) {
                                                      require(value >= type(int208).min && value <= type(int208).max, "SafeCast: value doesn't fit in 208 bits");
                                                      return int208(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int200 from int256, reverting on
                                                   * overflow (when the input is less than smallest int200 or
                                                   * greater than largest int200).
                                                   *
                                                   * Counterpart to Solidity's `int200` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 200 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt200(int256 value) internal pure returns (int200) {
                                                      require(value >= type(int200).min && value <= type(int200).max, "SafeCast: value doesn't fit in 200 bits");
                                                      return int200(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int192 from int256, reverting on
                                                   * overflow (when the input is less than smallest int192 or
                                                   * greater than largest int192).
                                                   *
                                                   * Counterpart to Solidity's `int192` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 192 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt192(int256 value) internal pure returns (int192) {
                                                      require(value >= type(int192).min && value <= type(int192).max, "SafeCast: value doesn't fit in 192 bits");
                                                      return int192(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int184 from int256, reverting on
                                                   * overflow (when the input is less than smallest int184 or
                                                   * greater than largest int184).
                                                   *
                                                   * Counterpart to Solidity's `int184` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 184 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt184(int256 value) internal pure returns (int184) {
                                                      require(value >= type(int184).min && value <= type(int184).max, "SafeCast: value doesn't fit in 184 bits");
                                                      return int184(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int176 from int256, reverting on
                                                   * overflow (when the input is less than smallest int176 or
                                                   * greater than largest int176).
                                                   *
                                                   * Counterpart to Solidity's `int176` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 176 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt176(int256 value) internal pure returns (int176) {
                                                      require(value >= type(int176).min && value <= type(int176).max, "SafeCast: value doesn't fit in 176 bits");
                                                      return int176(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int168 from int256, reverting on
                                                   * overflow (when the input is less than smallest int168 or
                                                   * greater than largest int168).
                                                   *
                                                   * Counterpart to Solidity's `int168` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 168 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt168(int256 value) internal pure returns (int168) {
                                                      require(value >= type(int168).min && value <= type(int168).max, "SafeCast: value doesn't fit in 168 bits");
                                                      return int168(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int160 from int256, reverting on
                                                   * overflow (when the input is less than smallest int160 or
                                                   * greater than largest int160).
                                                   *
                                                   * Counterpart to Solidity's `int160` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 160 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt160(int256 value) internal pure returns (int160) {
                                                      require(value >= type(int160).min && value <= type(int160).max, "SafeCast: value doesn't fit in 160 bits");
                                                      return int160(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int152 from int256, reverting on
                                                   * overflow (when the input is less than smallest int152 or
                                                   * greater than largest int152).
                                                   *
                                                   * Counterpart to Solidity's `int152` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 152 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt152(int256 value) internal pure returns (int152) {
                                                      require(value >= type(int152).min && value <= type(int152).max, "SafeCast: value doesn't fit in 152 bits");
                                                      return int152(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int144 from int256, reverting on
                                                   * overflow (when the input is less than smallest int144 or
                                                   * greater than largest int144).
                                                   *
                                                   * Counterpart to Solidity's `int144` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 144 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt144(int256 value) internal pure returns (int144) {
                                                      require(value >= type(int144).min && value <= type(int144).max, "SafeCast: value doesn't fit in 144 bits");
                                                      return int144(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int136 from int256, reverting on
                                                   * overflow (when the input is less than smallest int136 or
                                                   * greater than largest int136).
                                                   *
                                                   * Counterpart to Solidity's `int136` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 136 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt136(int256 value) internal pure returns (int136) {
                                                      require(value >= type(int136).min && value <= type(int136).max, "SafeCast: value doesn't fit in 136 bits");
                                                      return int136(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int128 from int256, reverting on
                                                   * overflow (when the input is less than smallest int128 or
                                                   * greater than largest int128).
                                                   *
                                                   * Counterpart to Solidity's `int128` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 128 bits
                                                   *
                                                   * _Available since v3.1._
                                                   */
                                                  function toInt128(int256 value) internal pure returns (int128) {
                                                      require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
                                                      return int128(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int120 from int256, reverting on
                                                   * overflow (when the input is less than smallest int120 or
                                                   * greater than largest int120).
                                                   *
                                                   * Counterpart to Solidity's `int120` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 120 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt120(int256 value) internal pure returns (int120) {
                                                      require(value >= type(int120).min && value <= type(int120).max, "SafeCast: value doesn't fit in 120 bits");
                                                      return int120(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int112 from int256, reverting on
                                                   * overflow (when the input is less than smallest int112 or
                                                   * greater than largest int112).
                                                   *
                                                   * Counterpart to Solidity's `int112` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 112 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt112(int256 value) internal pure returns (int112) {
                                                      require(value >= type(int112).min && value <= type(int112).max, "SafeCast: value doesn't fit in 112 bits");
                                                      return int112(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int104 from int256, reverting on
                                                   * overflow (when the input is less than smallest int104 or
                                                   * greater than largest int104).
                                                   *
                                                   * Counterpart to Solidity's `int104` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 104 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt104(int256 value) internal pure returns (int104) {
                                                      require(value >= type(int104).min && value <= type(int104).max, "SafeCast: value doesn't fit in 104 bits");
                                                      return int104(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int96 from int256, reverting on
                                                   * overflow (when the input is less than smallest int96 or
                                                   * greater than largest int96).
                                                   *
                                                   * Counterpart to Solidity's `int96` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 96 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt96(int256 value) internal pure returns (int96) {
                                                      require(value >= type(int96).min && value <= type(int96).max, "SafeCast: value doesn't fit in 96 bits");
                                                      return int96(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int88 from int256, reverting on
                                                   * overflow (when the input is less than smallest int88 or
                                                   * greater than largest int88).
                                                   *
                                                   * Counterpart to Solidity's `int88` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 88 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt88(int256 value) internal pure returns (int88) {
                                                      require(value >= type(int88).min && value <= type(int88).max, "SafeCast: value doesn't fit in 88 bits");
                                                      return int88(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int80 from int256, reverting on
                                                   * overflow (when the input is less than smallest int80 or
                                                   * greater than largest int80).
                                                   *
                                                   * Counterpart to Solidity's `int80` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 80 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt80(int256 value) internal pure returns (int80) {
                                                      require(value >= type(int80).min && value <= type(int80).max, "SafeCast: value doesn't fit in 80 bits");
                                                      return int80(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int72 from int256, reverting on
                                                   * overflow (when the input is less than smallest int72 or
                                                   * greater than largest int72).
                                                   *
                                                   * Counterpart to Solidity's `int72` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 72 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt72(int256 value) internal pure returns (int72) {
                                                      require(value >= type(int72).min && value <= type(int72).max, "SafeCast: value doesn't fit in 72 bits");
                                                      return int72(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int64 from int256, reverting on
                                                   * overflow (when the input is less than smallest int64 or
                                                   * greater than largest int64).
                                                   *
                                                   * Counterpart to Solidity's `int64` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 64 bits
                                                   *
                                                   * _Available since v3.1._
                                                   */
                                                  function toInt64(int256 value) internal pure returns (int64) {
                                                      require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
                                                      return int64(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int56 from int256, reverting on
                                                   * overflow (when the input is less than smallest int56 or
                                                   * greater than largest int56).
                                                   *
                                                   * Counterpart to Solidity's `int56` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 56 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt56(int256 value) internal pure returns (int56) {
                                                      require(value >= type(int56).min && value <= type(int56).max, "SafeCast: value doesn't fit in 56 bits");
                                                      return int56(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int48 from int256, reverting on
                                                   * overflow (when the input is less than smallest int48 or
                                                   * greater than largest int48).
                                                   *
                                                   * Counterpart to Solidity's `int48` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 48 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt48(int256 value) internal pure returns (int48) {
                                                      require(value >= type(int48).min && value <= type(int48).max, "SafeCast: value doesn't fit in 48 bits");
                                                      return int48(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int40 from int256, reverting on
                                                   * overflow (when the input is less than smallest int40 or
                                                   * greater than largest int40).
                                                   *
                                                   * Counterpart to Solidity's `int40` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 40 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt40(int256 value) internal pure returns (int40) {
                                                      require(value >= type(int40).min && value <= type(int40).max, "SafeCast: value doesn't fit in 40 bits");
                                                      return int40(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int32 from int256, reverting on
                                                   * overflow (when the input is less than smallest int32 or
                                                   * greater than largest int32).
                                                   *
                                                   * Counterpart to Solidity's `int32` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 32 bits
                                                   *
                                                   * _Available since v3.1._
                                                   */
                                                  function toInt32(int256 value) internal pure returns (int32) {
                                                      require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
                                                      return int32(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int24 from int256, reverting on
                                                   * overflow (when the input is less than smallest int24 or
                                                   * greater than largest int24).
                                                   *
                                                   * Counterpart to Solidity's `int24` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 24 bits
                                                   *
                                                   * _Available since v4.7._
                                                   */
                                                  function toInt24(int256 value) internal pure returns (int24) {
                                                      require(value >= type(int24).min && value <= type(int24).max, "SafeCast: value doesn't fit in 24 bits");
                                                      return int24(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int16 from int256, reverting on
                                                   * overflow (when the input is less than smallest int16 or
                                                   * greater than largest int16).
                                                   *
                                                   * Counterpart to Solidity's `int16` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 16 bits
                                                   *
                                                   * _Available since v3.1._
                                                   */
                                                  function toInt16(int256 value) internal pure returns (int16) {
                                                      require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
                                                      return int16(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the downcasted int8 from int256, reverting on
                                                   * overflow (when the input is less than smallest int8 or
                                                   * greater than largest int8).
                                                   *
                                                   * Counterpart to Solidity's `int8` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must fit into 8 bits
                                                   *
                                                   * _Available since v3.1._
                                                   */
                                                  function toInt8(int256 value) internal pure returns (int8) {
                                                      require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
                                                      return int8(value);
                                                  }
                                              
                                                  /**
                                                   * @dev Converts an unsigned uint256 into a signed int256.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - input must be less than or equal to maxInt256.
                                                   *
                                                   * _Available since v3.0._
                                                   */
                                                  function toInt256(uint256 value) internal pure returns (int256) {
                                                      // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                                                      require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                                                      return int256(value);
                                                  }
                                              }
                                              
                                              
                                              // File contracts/routers/UnoswapV3Router.sol
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              contract UnoswapV3Router is EthReceiver, IUniswapV3SwapCallback {
                                                  using Address for address payable;
                                                  using SafeERC20 for IERC20;
                                              
                                                  error EmptyPools();
                                                  error BadPool();
                                              
                                                  uint256 private constant _ONE_FOR_ZERO_MASK = 1 << 255;
                                                  uint256 private constant _WETH_UNWRAP_MASK = 1 << 253;
                                                  bytes32 private constant _POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
                                                  bytes32 private constant _FF_FACTORY = 0xff1F98431c8aD98523631AE4a59f267346ea31F9840000000000000000000000;
                                                  // concatenation of token0(), token1() fee(), transfer() and transferFrom() selectors
                                                  bytes32 private constant _SELECTORS = 0x0dfe1681d21220a7ddca3f43a9059cbb23b872dd000000000000000000000000;
                                                  uint256 private constant _ADDRESS_MASK =   0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
                                                  /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
                                                  uint160 private constant _MIN_SQRT_RATIO = 4295128739 + 1;
                                                  /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
                                                  uint160 private constant _MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342 - 1;
                                                  IWETH private immutable _WETH;  // solhint-disable-line var-name-mixedcase
                                              
                                                  constructor(IWETH weth) {
                                                      _WETH = weth;
                                                  }
                                              
                                                  /// @notice Same as `uniswapV3SwapTo` but calls permit first,
                                                  /// allowing to approve token spending and make a swap in one transaction.
                                                  /// @param recipient Address that will receive swap funds
                                                  /// @param srcToken Source token
                                                  /// @param amount Amount of source tokens to swap
                                                  /// @param minReturn Minimal allowed returnAmount to make transaction commit
                                                  /// @param pools Pools chain used for swaps. Pools src and dst tokens should match to make swap happen
                                                  /// @param permit Should contain valid permit that can be used in `IERC20Permit.permit` calls.
                                                  /// See tests for examples
                                                  function uniswapV3SwapToWithPermit(
                                                      address payable recipient,
                                                      IERC20 srcToken,
                                                      uint256 amount,
                                                      uint256 minReturn,
                                                      uint256[] calldata pools,
                                                      bytes calldata permit
                                                  ) external returns(uint256 returnAmount) {
                                                      srcToken.safePermit(permit);
                                                      return _uniswapV3Swap(recipient, amount, minReturn, pools);
                                                  }
                                              
                                                  /// @notice Same as `uniswapV3SwapTo` but uses `msg.sender` as recipient
                                                  /// @param amount Amount of source tokens to swap
                                                  /// @param minReturn Minimal allowed returnAmount to make transaction commit
                                                  /// @param pools Pools chain used for swaps. Pools src and dst tokens should match to make swap happen
                                                  function uniswapV3Swap(
                                                      uint256 amount,
                                                      uint256 minReturn,
                                                      uint256[] calldata pools
                                                  ) external payable returns(uint256 returnAmount) {
                                                      return _uniswapV3Swap(payable(msg.sender), amount, minReturn, pools);
                                                  }
                                              
                                                  /// @notice Performs swap using Uniswap V3 exchange. Wraps and unwraps ETH if required.
                                                  /// Sending non-zero `msg.value` for anything but ETH swaps is prohibited
                                                  /// @param recipient Address that will receive swap funds
                                                  /// @param amount Amount of source tokens to swap
                                                  /// @param minReturn Minimal allowed returnAmount to make transaction commit
                                                  /// @param pools Pools chain used for swaps. Pools src and dst tokens should match to make swap happen
                                                  function uniswapV3SwapTo(
                                                      address payable recipient,
                                                      uint256 amount,
                                                      uint256 minReturn,
                                                      uint256[] calldata pools
                                                  ) external payable returns(uint256 returnAmount) {
                                                      return _uniswapV3Swap(recipient, amount, minReturn, pools);
                                                  }
                                              
                                                  function _uniswapV3Swap(
                                                      address payable recipient,
                                                      uint256 amount,
                                                      uint256 minReturn,
                                                      uint256[] calldata pools
                                                  ) private returns(uint256 returnAmount) {
                                                      unchecked {
                                                          uint256 len = pools.length;
                                                          if (len == 0) revert EmptyPools();
                                                          uint256 lastIndex = len - 1;
                                                          returnAmount = amount;
                                                          bool wrapWeth = msg.value > 0;
                                                          bool unwrapWeth = pools[lastIndex] & _WETH_UNWRAP_MASK > 0;
                                                          if (wrapWeth) {
                                                              if (msg.value != amount) revert RouterErrors.InvalidMsgValue();
                                                              _WETH.deposit{value: amount}();
                                                          }
                                                          if (len > 1) {
                                                              returnAmount = _makeSwap(address(this), wrapWeth ? address(this) : msg.sender, pools[0], returnAmount);
                                              
                                                              for (uint256 i = 1; i < lastIndex; i++) {
                                                                  returnAmount = _makeSwap(address(this), address(this), pools[i], returnAmount);
                                                              }
                                                              returnAmount = _makeSwap(unwrapWeth ? address(this) : recipient, address(this), pools[lastIndex], returnAmount);
                                                          } else {
                                                              returnAmount = _makeSwap(unwrapWeth ? address(this) : recipient, wrapWeth ? address(this) : msg.sender, pools[0], returnAmount);
                                                          }
                                              
                                                          if (returnAmount < minReturn) revert RouterErrors.ReturnAmountIsNotEnough();
                                              
                                                          if (unwrapWeth) {
                                                              _WETH.withdraw(returnAmount);
                                                              recipient.sendValue(returnAmount);
                                                          }
                                                      }
                                                  }
                                              
                                                  /// @inheritdoc IUniswapV3SwapCallback
                                                  function uniswapV3SwapCallback(
                                                      int256 amount0Delta,
                                                      int256 amount1Delta,
                                                      bytes calldata /* data */
                                                  ) external override {
                                                      assembly {  // solhint-disable-line no-inline-assembly
                                                          function reRevert() {
                                                              returndatacopy(0, 0, returndatasize())
                                                              revert(0, returndatasize())
                                                          }
                                              
                                                          function validateERC20Transfer(status) {
                                                              if iszero(status) {
                                                                  reRevert()
                                                              }
                                                              let success := or(
                                                                  iszero(returndatasize()),                       // empty return data
                                                                  and(gt(returndatasize(), 31), eq(mload(0), 1))  // true in return data
                                                              )
                                                              if iszero(success) {
                                                                  mstore(0, 0xf27f64e400000000000000000000000000000000000000000000000000000000)  // ERC20TransferFailed()
                                                                  revert(0, 4)
                                                              }
                                                          }
                                              
                                                          let emptyPtr := mload(0x40)
                                                          let resultPtr := add(emptyPtr, 0x15)  // 0x15 = _FF_FACTORY size
                                              
                                                          mstore(emptyPtr, _SELECTORS)
                                                          if iszero(staticcall(gas(), caller(), emptyPtr, 0x4, resultPtr, 0x20)) {
                                                              reRevert()
                                                          }
                                                          if iszero(staticcall(gas(), caller(), add(emptyPtr, 0x4), 0x4, add(resultPtr, 0x20), 0x20)) {
                                                              reRevert()
                                                          }
                                                          if iszero(staticcall(gas(), caller(), add(emptyPtr, 0x8), 0x4, add(resultPtr, 0x40), 0x20)) {
                                                              reRevert()
                                                          }
                                              
                                                          let token
                                                          let amount
                                                          switch sgt(amount0Delta, 0)
                                                          case 1 {
                                                              token := mload(resultPtr)
                                                              amount := amount0Delta
                                                          }
                                                          default {
                                                              token := mload(add(resultPtr, 0x20))
                                                              amount := amount1Delta
                                                          }
                                              
                                                          mstore(emptyPtr, _FF_FACTORY)
                                                          mstore(resultPtr, keccak256(resultPtr, 0x60)) // Compute the inner hash in-place
                                                          mstore(add(resultPtr, 0x20), _POOL_INIT_CODE_HASH)
                                                          let pool := and(keccak256(emptyPtr, 0x55), _ADDRESS_MASK)
                                                          if xor(pool, caller()) {
                                                              mstore(0, 0xb2c0272200000000000000000000000000000000000000000000000000000000)  // BadPool()
                                                              revert(0, 4)
                                                          }
                                              
                                                          let payer := calldataload(0x84)
                                                          mstore(emptyPtr, _SELECTORS)
                                                          switch eq(payer, address())
                                                          case 1 {
                                                              // token.safeTransfer(msg.sender,amount)
                                                              mstore(add(emptyPtr, 0x10), caller())
                                                              mstore(add(emptyPtr, 0x30), amount)
                                                              validateERC20Transfer(
                                                                  call(gas(), token, 0, add(emptyPtr, 0x0c), 0x44, 0, 0x20)
                                                              )
                                                          }
                                                          default {
                                                              // token.safeTransferFrom(payer, msg.sender, amount);
                                                              mstore(add(emptyPtr, 0x14), payer)
                                                              mstore(add(emptyPtr, 0x34), caller())
                                                              mstore(add(emptyPtr, 0x54), amount)
                                                              validateERC20Transfer(
                                                                  call(gas(), token, 0, add(emptyPtr, 0x10), 0x64, 0, 0x20)
                                                              )
                                                          }
                                                      }
                                                  }
                                              
                                                  function _makeSwap(address recipient, address payer, uint256 pool, uint256 amount) private returns (uint256) {
                                                      bool zeroForOne = pool & _ONE_FOR_ZERO_MASK == 0;
                                                      if (zeroForOne) {
                                                          (, int256 amount1) = IUniswapV3Pool(address(uint160(pool))).swap(
                                                              recipient,
                                                              zeroForOne,
                                                              SafeCast.toInt256(amount),
                                                              _MIN_SQRT_RATIO,
                                                              abi.encode(payer)
                                                          );
                                                          return SafeCast.toUint256(-amount1);
                                                      } else {
                                                          (int256 amount0,) = IUniswapV3Pool(address(uint160(pool))).swap(
                                                              recipient,
                                                              zeroForOne,
                                                              SafeCast.toInt256(amount),
                                                              _MAX_SQRT_RATIO,
                                                              abi.encode(payer)
                                                          );
                                                          return SafeCast.toUint256(-amount0);
                                                      }
                                                  }
                                              }
                                              
                                              
                                              // File @1inch/solidity-utils/contracts/[email protected]
                                              
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              abstract contract OnlyWethReceiver is EthReceiver {
                                                  address private immutable _WETH;  // solhint-disable-line var-name-mixedcase
                                              
                                                  constructor(address weth) {
                                                      _WETH = address(weth);
                                                  }
                                              
                                                  function _receive() internal virtual override {
                                                      if (msg.sender != _WETH) revert EthDepositRejected();
                                                  }
                                              }
                                              
                                              
                                              // File @openzeppelin/contracts/interfaces/[email protected]
                                              
                                              // OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              /**
                                               * @dev Interface of the ERC1271 standard signature validation method for
                                               * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
                                               *
                                               * _Available since v4.1._
                                               */
                                              interface IERC1271 {
                                                  /**
                                                   * @dev Should return whether the signature provided is valid for the provided data
                                                   * @param hash      Hash of the data to be signed
                                                   * @param signature Signature byte array associated with _data
                                                   */
                                                  function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
                                              }
                                              
                                              
                                              // File @1inch/solidity-utils/contracts/libraries/[email protected]
                                              
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              library ECDSA {
                                                  // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                                                  // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                                                  // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
                                                  // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                                                  //
                                                  // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                                                  // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                                                  // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                                                  // these malleable signatures as well.
                                                  uint256 private constant _S_BOUNDARY = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 + 1;
                                                  uint256 private constant _COMPACT_S_MASK = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
                                                  uint256 private constant _COMPACT_V_SHIFT = 255;
                                              
                                                  function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns(address signer) {
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          if lt(s, _S_BOUNDARY) {
                                                              let ptr := mload(0x40)
                                              
                                                              mstore(ptr, hash)
                                                              mstore(add(ptr, 0x20), v)
                                                              mstore(add(ptr, 0x40), r)
                                                              mstore(add(ptr, 0x60), s)
                                                              mstore(0, 0)
                                                              pop(staticcall(gas(), 0x1, ptr, 0x80, 0, 0x20))
                                                              signer := mload(0)
                                                          }
                                                      }
                                                  }
                                              
                                                  function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns(address signer) {
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let s := and(vs, _COMPACT_S_MASK)
                                                          if lt(s, _S_BOUNDARY) {
                                                              let ptr := mload(0x40)
                                              
                                                              mstore(ptr, hash)
                                                              mstore(add(ptr, 0x20), add(27, shr(_COMPACT_V_SHIFT, vs)))
                                                              mstore(add(ptr, 0x40), r)
                                                              mstore(add(ptr, 0x60), s)
                                                              mstore(0, 0)
                                                              pop(staticcall(gas(), 0x1, ptr, 0x80, 0, 0x20))
                                                              signer := mload(0)
                                                          }
                                                      }
                                                  }
                                              
                                                  /// WARNING!!!
                                                  /// There is a known signature malleability issue with two representations of signatures!
                                                  /// Even though this function is able to verify both standard 65-byte and compact 64-byte EIP-2098 signatures
                                                  /// one should never use raw signatures for any kind of invalidation logic in their code.
                                                  /// As the standard and compact representations are interchangeable any invalidation logic that relies on
                                                  /// signature uniqueness will get rekt.
                                                  /// More info: https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-4h98-2769-gh6h
                                                  function recover(bytes32 hash, bytes calldata signature) internal view returns(address signer) {
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let ptr := mload(0x40)
                                              
                                                          // memory[ptr:ptr+0x80] = (hash, v, r, s)
                                                          switch signature.length
                                                          case 65 {
                                                              // memory[ptr+0x20:ptr+0x80] = (v, r, s)
                                                              mstore(add(ptr, 0x20), byte(0, calldataload(add(signature.offset, 0x40))))
                                                              calldatacopy(add(ptr, 0x40), signature.offset, 0x40)
                                                          }
                                                          case 64 {
                                                              // memory[ptr+0x20:ptr+0x80] = (v, r, s)
                                                              let vs := calldataload(add(signature.offset, 0x20))
                                                              mstore(add(ptr, 0x20), add(27, shr(_COMPACT_V_SHIFT, vs)))
                                                              calldatacopy(add(ptr, 0x40), signature.offset, 0x20)
                                                              mstore(add(ptr, 0x60), and(vs, _COMPACT_S_MASK))
                                                          }
                                                          default {
                                                              ptr := 0
                                                          }
                                              
                                                          if ptr {
                                                              if lt(mload(add(ptr, 0x60)), _S_BOUNDARY) {
                                                                  // memory[ptr:ptr+0x20] = (hash)
                                                                  mstore(ptr, hash)
                                              
                                                                  mstore(0, 0)
                                                                  pop(staticcall(gas(), 0x1, ptr, 0x80, 0, 0x20))
                                                                  signer := mload(0)
                                                              }
                                                          }
                                                      }
                                                  }
                                              
                                                  function recoverOrIsValidSignature(address signer, bytes32 hash, bytes calldata signature) internal view returns(bool success) {
                                                      if (signer == address(0)) return false;
                                                      if ((signature.length == 64 || signature.length == 65) && recover(hash, signature) == signer) {
                                                          return true;
                                                      }
                                                      return isValidSignature(signer, hash, signature);
                                                  }
                                              
                                                  function recoverOrIsValidSignature(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns(bool success) {
                                                      if (signer == address(0)) return false;
                                                      if (recover(hash, v, r, s) == signer) {
                                                          return true;
                                                      }
                                                      return isValidSignature(signer, hash, v, r, s);
                                                  }
                                              
                                                  function recoverOrIsValidSignature(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns(bool success) {
                                                      if (signer == address(0)) return false;
                                                      if (recover(hash, r, vs) == signer) {
                                                          return true;
                                                      }
                                                      return isValidSignature(signer, hash, r, vs);
                                                  }
                                              
                                                  function recoverOrIsValidSignature65(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns(bool success) {
                                                      if (signer == address(0)) return false;
                                                      if (recover(hash, r, vs) == signer) {
                                                          return true;
                                                      }
                                                      return isValidSignature65(signer, hash, r, vs);
                                                  }
                                              
                                                  function isValidSignature(address signer, bytes32 hash, bytes calldata signature) internal view returns(bool success) {
                                                      // (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature));
                                                      // return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
                                                      bytes4 selector = IERC1271.isValidSignature.selector;
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let ptr := mload(0x40)
                                              
                                                          mstore(ptr, selector)
                                                          mstore(add(ptr, 0x04), hash)
                                                          mstore(add(ptr, 0x24), 0x40)
                                                          mstore(add(ptr, 0x44), signature.length)
                                                          calldatacopy(add(ptr, 0x64), signature.offset, signature.length)
                                                          if staticcall(gas(), signer, ptr, add(0x64, signature.length), 0, 0x20) {
                                                              success := and(eq(selector, mload(0)), eq(returndatasize(), 0x20))
                                                          }
                                                      }
                                                  }
                                              
                                                  function isValidSignature(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns(bool success) {
                                                      bytes4 selector = IERC1271.isValidSignature.selector;
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let ptr := mload(0x40)
                                              
                                                          mstore(ptr, selector)
                                                          mstore(add(ptr, 0x04), hash)
                                                          mstore(add(ptr, 0x24), 0x40)
                                                          mstore(add(ptr, 0x44), 65)
                                                          mstore(add(ptr, 0x64), r)
                                                          mstore(add(ptr, 0x84), s)
                                                          mstore8(add(ptr, 0xa4), v)
                                                          if staticcall(gas(), signer, ptr, 0xa5, 0, 0x20) {
                                                              success := and(eq(selector, mload(0)), eq(returndatasize(), 0x20))
                                                          }
                                                      }
                                                  }
                                              
                                                  function isValidSignature(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns(bool success) {
                                                      // (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, abi.encodePacked(r, vs)));
                                                      // return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
                                                      bytes4 selector = IERC1271.isValidSignature.selector;
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let ptr := mload(0x40)
                                              
                                                          mstore(ptr, selector)
                                                          mstore(add(ptr, 0x04), hash)
                                                          mstore(add(ptr, 0x24), 0x40)
                                                          mstore(add(ptr, 0x44), 64)
                                                          mstore(add(ptr, 0x64), r)
                                                          mstore(add(ptr, 0x84), vs)
                                                          if staticcall(gas(), signer, ptr, 0xa4, 0, 0x20) {
                                                              success := and(eq(selector, mload(0)), eq(returndatasize(), 0x20))
                                                          }
                                                      }
                                                  }
                                              
                                                  function isValidSignature65(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns(bool success) {
                                                      // (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, abi.encodePacked(r, vs & ~uint256(1 << 255), uint8(vs >> 255))));
                                                      // return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
                                                      bytes4 selector = IERC1271.isValidSignature.selector;
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let ptr := mload(0x40)
                                              
                                                          mstore(ptr, selector)
                                                          mstore(add(ptr, 0x04), hash)
                                                          mstore(add(ptr, 0x24), 0x40)
                                                          mstore(add(ptr, 0x44), 65)
                                                          mstore(add(ptr, 0x64), r)
                                                          mstore(add(ptr, 0x84), and(vs, _COMPACT_S_MASK))
                                                          mstore8(add(ptr, 0xa4), add(27, shr(_COMPACT_V_SHIFT, vs)))
                                                          if staticcall(gas(), signer, ptr, 0xa5, 0, 0x20) {
                                                              success := and(eq(selector, mload(0)), eq(returndatasize(), 0x20))
                                                          }
                                                      }
                                                  }
                                              
                                                  function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 res) {
                                                      // 32 is the length in bytes of hash, enforced by the type signature above
                                                      // return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          mstore(0, 0x19457468657265756d205369676e6564204d6573736167653a0a333200000000) // "\x19Ethereum Signed Message:\n32"
                                                          mstore(28, hash)
                                                          res := keccak256(0, 60)
                                                      }
                                                  }
                                              
                                                  function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 res) {
                                                      // return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let ptr := mload(0x40)
                                                          mstore(ptr, 0x1901000000000000000000000000000000000000000000000000000000000000) // "\x19\x01"
                                                          mstore(add(ptr, 0x02), domainSeparator)
                                                          mstore(add(ptr, 0x22), structHash)
                                                          res := keccak256(ptr, 66)
                                                      }
                                                  }
                                              }
                                              
                                              
                                              // File @1inch/limit-order-protocol/contracts/[email protected]
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              library OrderRFQLib {
                                                  struct OrderRFQ {
                                                      uint256 info;  // lowest 64 bits is the order id, next 64 bits is the expiration timestamp
                                                      address makerAsset;
                                                      address takerAsset;
                                                      address maker;
                                                      address allowedSender;  // equals to Zero address on public orders
                                                      uint256 makingAmount;
                                                      uint256 takingAmount;
                                                  }
                                              
                                                  bytes32 constant internal _LIMIT_ORDER_RFQ_TYPEHASH = keccak256(
                                                      "OrderRFQ("
                                                          "uint256 info,"
                                                          "address makerAsset,"
                                                          "address takerAsset,"
                                                          "address maker,"
                                                          "address allowedSender,"
                                                          "uint256 makingAmount,"
                                                          "uint256 takingAmount"
                                                      ")"
                                                  );
                                              
                                                  function hash(OrderRFQ memory order, bytes32 domainSeparator) internal pure returns(bytes32 result) {
                                                      bytes32 typehash = _LIMIT_ORDER_RFQ_TYPEHASH;
                                                      bytes32 orderHash;
                                                      // this assembly is memory unsafe :(
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let ptr := sub(order, 0x20)
                                              
                                                          // keccak256(abi.encode(_LIMIT_ORDER_RFQ_TYPEHASH, order));
                                                          let tmp := mload(ptr)
                                                          mstore(ptr, typehash)
                                                          orderHash := keccak256(ptr, 0x100)
                                                          mstore(ptr, tmp)
                                                      }
                                                      return ECDSA.toTypedDataHash(domainSeparator, orderHash);
                                                  }
                                              }
                                              
                                              
                                              // File @openzeppelin/contracts/utils/[email protected]
                                              
                                              // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              /**
                                               * @dev String operations.
                                               */
                                              library Strings {
                                                  bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
                                                  uint8 private constant _ADDRESS_LENGTH = 20;
                                              
                                                  /**
                                                   * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                                                   */
                                                  function toString(uint256 value) internal pure returns (string memory) {
                                                      // Inspired by OraclizeAPI's implementation - MIT licence
                                                      // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
                                              
                                                      if (value == 0) {
                                                          return "0";
                                                      }
                                                      uint256 temp = value;
                                                      uint256 digits;
                                                      while (temp != 0) {
                                                          digits++;
                                                          temp /= 10;
                                                      }
                                                      bytes memory buffer = new bytes(digits);
                                                      while (value != 0) {
                                                          digits -= 1;
                                                          buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                                                          value /= 10;
                                                      }
                                                      return string(buffer);
                                                  }
                                              
                                                  /**
                                                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                                                   */
                                                  function toHexString(uint256 value) internal pure returns (string memory) {
                                                      if (value == 0) {
                                                          return "0x00";
                                                      }
                                                      uint256 temp = value;
                                                      uint256 length = 0;
                                                      while (temp != 0) {
                                                          length++;
                                                          temp >>= 8;
                                                      }
                                                      return toHexString(value, length);
                                                  }
                                              
                                                  /**
                                                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                                                   */
                                                  function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                                                      bytes memory buffer = new bytes(2 * length + 2);
                                                      buffer[0] = "0";
                                                      buffer[1] = "x";
                                                      for (uint256 i = 2 * length + 1; i > 1; --i) {
                                                          buffer[i] = _HEX_SYMBOLS[value & 0xf];
                                                          value >>= 4;
                                                      }
                                                      require(value == 0, "Strings: hex length insufficient");
                                                      return string(buffer);
                                                  }
                                              
                                                  /**
                                                   * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                                                   */
                                                  function toHexString(address addr) internal pure returns (string memory) {
                                                      return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                                                  }
                                              }
                                              
                                              
                                              // File @openzeppelin/contracts/utils/cryptography/[email protected]
                                              
                                              // OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              /**
                                               * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
                                               *
                                               * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
                                               * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
                                               * they need in their contracts using a combination of `abi.encode` and `keccak256`.
                                               *
                                               * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
                                               * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
                                               * ({_hashTypedDataV4}).
                                               *
                                               * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
                                               * the chain id to protect against replay attacks on an eventual fork of the chain.
                                               *
                                               * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
                                               * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
                                               *
                                               * _Available since v3.4._
                                               */
                                              abstract contract EIP712 {
                                                  /* solhint-disable var-name-mixedcase */
                                                  // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
                                                  // invalidate the cached domain separator if the chain id changes.
                                                  bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
                                                  uint256 private immutable _CACHED_CHAIN_ID;
                                                  address private immutable _CACHED_THIS;
                                              
                                                  bytes32 private immutable _HASHED_NAME;
                                                  bytes32 private immutable _HASHED_VERSION;
                                                  bytes32 private immutable _TYPE_HASH;
                                              
                                                  /* solhint-enable var-name-mixedcase */
                                              
                                                  /**
                                                   * @dev Initializes the domain separator and parameter caches.
                                                   *
                                                   * The meaning of `name` and `version` is specified in
                                                   * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                                                   *
                                                   * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                                                   * - `version`: the current major version of the signing domain.
                                                   *
                                                   * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                                                   * contract upgrade].
                                                   */
                                                  constructor(string memory name, string memory version) {
                                                      bytes32 hashedName = keccak256(bytes(name));
                                                      bytes32 hashedVersion = keccak256(bytes(version));
                                                      bytes32 typeHash = keccak256(
                                                          "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                                                      );
                                                      _HASHED_NAME = hashedName;
                                                      _HASHED_VERSION = hashedVersion;
                                                      _CACHED_CHAIN_ID = block.chainid;
                                                      _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
                                                      _CACHED_THIS = address(this);
                                                      _TYPE_HASH = typeHash;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the domain separator for the current chain.
                                                   */
                                                  function _domainSeparatorV4() internal view returns (bytes32) {
                                                      if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
                                                          return _CACHED_DOMAIN_SEPARATOR;
                                                      } else {
                                                          return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
                                                      }
                                                  }
                                              
                                                  function _buildDomainSeparator(
                                                      bytes32 typeHash,
                                                      bytes32 nameHash,
                                                      bytes32 versionHash
                                                  ) private view returns (bytes32) {
                                                      return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
                                                  }
                                              
                                                  /**
                                                   * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                                                   * function returns the hash of the fully encoded EIP712 message for this domain.
                                                   *
                                                   * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                                                   *
                                                   * ```solidity
                                                   * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                                                   *     keccak256("Mail(address to,string contents)"),
                                                   *     mailTo,
                                                   *     keccak256(bytes(mailContents))
                                                   * )));
                                                   * address signer = ECDSA.recover(digest, signature);
                                                   * ```
                                                   */
                                                  function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                                                      return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
                                                  }
                                              }
                                              
                                              
                                              // File @1inch/limit-order-protocol/contracts/libraries/[email protected]
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              library Errors {
                                                  error InvalidMsgValue();
                                                  error ETHTransferFailed();
                                              }
                                              
                                              
                                              // File @1inch/limit-order-protocol/contracts/helpers/[email protected]
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              /// @title A helper contract for calculations related to order amounts
                                              library AmountCalculator {
                                                  /// @notice Calculates maker amount
                                                  /// @return Result Floored maker amount
                                                  function getMakingAmount(uint256 orderMakerAmount, uint256 orderTakerAmount, uint256 swapTakerAmount) internal pure returns(uint256) {
                                                      return swapTakerAmount * orderMakerAmount / orderTakerAmount;
                                                  }
                                              
                                                  /// @notice Calculates taker amount
                                                  /// @return Result Ceiled taker amount
                                                  function getTakingAmount(uint256 orderMakerAmount, uint256 orderTakerAmount, uint256 swapMakerAmount) internal pure returns(uint256) {
                                                      return (swapMakerAmount * orderTakerAmount + orderMakerAmount - 1) / orderMakerAmount;
                                                  }
                                              }
                                              
                                              
                                              // File @1inch/limit-order-protocol/contracts/[email protected]
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              /// @title RFQ Limit Order mixin
                                              abstract contract OrderRFQMixin is EIP712, OnlyWethReceiver {
                                                  using SafeERC20 for IERC20;
                                                  using OrderRFQLib for OrderRFQLib.OrderRFQ;
                                              
                                                  error RFQZeroTargetIsForbidden();
                                                  error RFQPrivateOrder();
                                                  error RFQBadSignature();
                                                  error OrderExpired();
                                                  error MakingAmountExceeded();
                                                  error TakingAmountExceeded();
                                                  error RFQSwapWithZeroAmount();
                                                  error InvalidatedOrder();
                                              
                                                  /**
                                                   * @notice Emitted when RFQ gets filled
                                                   * @param orderHash Hash of the order
                                                   * @param makingAmount Amount of the maker asset that was transferred from maker to taker
                                                   */
                                                  event OrderFilledRFQ(
                                                      bytes32 orderHash,
                                                      uint256 makingAmount
                                                  );
                                              
                                                  uint256 private constant _RAW_CALL_GAS_LIMIT = 5000;
                                                  uint256 private constant _MAKER_AMOUNT_FLAG = 1 << 255;
                                                  uint256 private constant _SIGNER_SMART_CONTRACT_HINT = 1 << 254;
                                                  uint256 private constant _IS_VALID_SIGNATURE_65_BYTES = 1 << 253;
                                                  uint256 private constant _UNWRAP_WETH_FLAG = 1 << 252;
                                                  uint256 private constant _AMOUNT_MASK = ~(
                                                      _MAKER_AMOUNT_FLAG |
                                                      _SIGNER_SMART_CONTRACT_HINT |
                                                      _IS_VALID_SIGNATURE_65_BYTES |
                                                      _UNWRAP_WETH_FLAG
                                                  );
                                              
                                                  IWETH private immutable _WETH;  // solhint-disable-line var-name-mixedcase
                                                  mapping(address => mapping(uint256 => uint256)) private _invalidator;
                                              
                                                  constructor(IWETH weth) OnlyWethReceiver(address(weth)) {
                                                      _WETH = weth;
                                                  }
                                              
                                                  /**
                                                   * @notice Returns bitmask for double-spend invalidators based on lowest byte of order.info and filled quotes
                                                   * @param maker Maker address
                                                   * @param slot Slot number to return bitmask for
                                                   * @return result Each bit represents whether corresponding was already invalidated
                                                   */
                                                  function invalidatorForOrderRFQ(address maker, uint256 slot) external view returns(uint256 /* result */) {
                                                      return _invalidator[maker][slot];
                                                  }
                                              
                                                  /**
                                                   * @notice Cancels order's quote
                                                   * @param orderInfo Order info (only order id in lowest 64 bits is used)
                                                   */
                                                  function cancelOrderRFQ(uint256 orderInfo) external {
                                                      _invalidateOrder(msg.sender, orderInfo, 0);
                                                  }
                                              
                                                  /// @notice Cancels multiple order's quotes
                                                  function cancelOrderRFQ(uint256 orderInfo, uint256 additionalMask) external {
                                                      _invalidateOrder(msg.sender, orderInfo, additionalMask);
                                                  }
                                              
                                                  /**
                                                   * @notice Fills order's quote, fully or partially (whichever is possible)
                                                   * @param order Order quote to fill
                                                   * @param signature Signature to confirm quote ownership
                                                   * @param flagsAndAmount Fill configuration flags with amount packed in one slot
                                                   * @return filledMakingAmount Actual amount transferred from maker to taker
                                                   * @return filledTakingAmount Actual amount transferred from taker to maker
                                                   * @return orderHash Hash of the filled order
                                                   */
                                                  function fillOrderRFQ(
                                                      OrderRFQLib.OrderRFQ memory order,
                                                      bytes calldata signature,
                                                      uint256 flagsAndAmount
                                                  ) external payable returns(uint256 /* filledMakingAmount */, uint256 /* filledTakingAmount */, bytes32 /* orderHash */) {
                                                      return fillOrderRFQTo(order, signature, flagsAndAmount, msg.sender);
                                                  }
                                              
                                                  /**
                                                   * @notice Fills order's quote, fully or partially, with compact signature
                                                   * @param order Order quote to fill
                                                   * @param r R component of signature
                                                   * @param vs VS component of signature
                                                   * @param flagsAndAmount Fill configuration flags with amount packed in one slot
                                                   * - Bits 0-252 contain the amount to fill
                                                   * - Bit 253 is used to indicate whether signature is 64-bit (0) or 65-bit (1)
                                                   * - Bit 254 is used to indicate whether smart contract (1) signed the order or not (0)
                                                   * - Bit 255 is used to indicate whether maker (1) or taker amount (0) is given in the amount parameter
                                                   * @return filledMakingAmount Actual amount transferred from maker to taker
                                                   * @return filledTakingAmount Actual amount transferred from taker to maker
                                                   * @return orderHash Hash of the filled order
                                                   */
                                                  function fillOrderRFQCompact(
                                                      OrderRFQLib.OrderRFQ memory order,
                                                      bytes32 r,
                                                      bytes32 vs,
                                                      uint256 flagsAndAmount
                                                  ) external payable returns(uint256 filledMakingAmount, uint256 filledTakingAmount, bytes32 orderHash) {
                                                      orderHash = order.hash(_domainSeparatorV4());
                                                      if (flagsAndAmount & _SIGNER_SMART_CONTRACT_HINT != 0) {
                                                          if (flagsAndAmount & _IS_VALID_SIGNATURE_65_BYTES != 0) {
                                                              if (!ECDSA.isValidSignature65(order.maker, orderHash, r, vs)) revert RFQBadSignature();
                                                          } else {
                                                              if (!ECDSA.isValidSignature(order.maker, orderHash, r, vs)) revert RFQBadSignature();
                                                          }
                                                      } else {
                                                          if(!ECDSA.recoverOrIsValidSignature(order.maker, orderHash, r, vs)) revert RFQBadSignature();
                                                      }
                                              
                                                      (filledMakingAmount, filledTakingAmount) = _fillOrderRFQTo(order, flagsAndAmount, msg.sender);
                                                      emit OrderFilledRFQ(orderHash, filledMakingAmount);
                                                  }
                                              
                                                  /**
                                                   * @notice Same as `fillOrderRFQTo` but calls permit first.
                                                   * It allows to approve token spending and make a swap in one transaction.
                                                   * Also allows to specify funds destination instead of `msg.sender`
                                                   * @param order Order quote to fill
                                                   * @param signature Signature to confirm quote ownership
                                                   * @param flagsAndAmount Fill configuration flags with amount packed in one slot
                                                   * @param target Address that will receive swap funds
                                                   * @param permit Should consist of abiencoded token address and encoded `IERC20Permit.permit` call.
                                                   * @return filledMakingAmount Actual amount transferred from maker to taker
                                                   * @return filledTakingAmount Actual amount transferred from taker to maker
                                                   * @return orderHash Hash of the filled order
                                                   * @dev See tests for examples
                                                   */
                                                  function fillOrderRFQToWithPermit(
                                                      OrderRFQLib.OrderRFQ memory order,
                                                      bytes calldata signature,
                                                      uint256 flagsAndAmount,
                                                      address target,
                                                      bytes calldata permit
                                                  ) external returns(uint256 /* filledMakingAmount */, uint256 /* filledTakingAmount */, bytes32 /* orderHash */) {
                                                      IERC20(order.takerAsset).safePermit(permit);
                                                      return fillOrderRFQTo(order, signature, flagsAndAmount, target);
                                                  }
                                              
                                                  /**
                                                   * @notice Same as `fillOrderRFQ` but allows to specify funds destination instead of `msg.sender`
                                                   * @param order Order quote to fill
                                                   * @param signature Signature to confirm quote ownership
                                                   * @param flagsAndAmount Fill configuration flags with amount packed in one slot
                                                   * @param target Address that will receive swap funds
                                                   * @return filledMakingAmount Actual amount transferred from maker to taker
                                                   * @return filledTakingAmount Actual amount transferred from taker to maker
                                                   * @return orderHash Hash of the filled order
                                                   */
                                                  function fillOrderRFQTo(
                                                      OrderRFQLib.OrderRFQ memory order,
                                                      bytes calldata signature,
                                                      uint256 flagsAndAmount,
                                                      address target
                                                  ) public payable returns(uint256 filledMakingAmount, uint256 filledTakingAmount, bytes32 orderHash) {
                                                      orderHash = order.hash(_domainSeparatorV4());
                                                      if (flagsAndAmount & _SIGNER_SMART_CONTRACT_HINT != 0) {
                                                          if (flagsAndAmount & _IS_VALID_SIGNATURE_65_BYTES != 0 && signature.length != 65) revert RFQBadSignature();
                                                          if (!ECDSA.isValidSignature(order.maker, orderHash, signature)) revert RFQBadSignature();
                                                      } else {
                                                          if(!ECDSA.recoverOrIsValidSignature(order.maker, orderHash, signature)) revert RFQBadSignature();
                                                      }
                                                      (filledMakingAmount, filledTakingAmount) = _fillOrderRFQTo(order, flagsAndAmount, target);
                                                      emit OrderFilledRFQ(orderHash, filledMakingAmount);
                                                  }
                                              
                                                  function _fillOrderRFQTo(
                                                      OrderRFQLib.OrderRFQ memory order,
                                                      uint256 flagsAndAmount,
                                                      address target
                                                  ) private returns(uint256 makingAmount, uint256 takingAmount) {
                                                      if (target == address(0)) revert RFQZeroTargetIsForbidden();
                                              
                                                      address maker = order.maker;
                                              
                                                      // Validate order
                                                      if (order.allowedSender != address(0) && order.allowedSender != msg.sender) revert RFQPrivateOrder();
                                              
                                                      {  // Stack too deep
                                                          uint256 info = order.info;
                                                          // Check time expiration
                                                          uint256 expiration = uint128(info) >> 64;
                                                          if (expiration != 0 && block.timestamp > expiration) revert OrderExpired(); // solhint-disable-line not-rely-on-time
                                                          _invalidateOrder(maker, info, 0);
                                                      }
                                              
                                                      {  // Stack too deep
                                                          uint256 orderMakingAmount = order.makingAmount;
                                                          uint256 orderTakingAmount = order.takingAmount;
                                                          uint256 amount = flagsAndAmount & _AMOUNT_MASK;
                                                          // Compute partial fill if needed
                                                          if (amount == 0) {
                                                              // zero amount means whole order
                                                              makingAmount = orderMakingAmount;
                                                              takingAmount = orderTakingAmount;
                                                          }
                                                          else if (flagsAndAmount & _MAKER_AMOUNT_FLAG != 0) {
                                                              if (amount > orderMakingAmount) revert MakingAmountExceeded();
                                                              makingAmount = amount;
                                                              takingAmount = AmountCalculator.getTakingAmount(orderMakingAmount, orderTakingAmount, makingAmount);
                                                          }
                                                          else {
                                                              if (amount > orderTakingAmount) revert TakingAmountExceeded();
                                                              takingAmount = amount;
                                                              makingAmount = AmountCalculator.getMakingAmount(orderMakingAmount, orderTakingAmount, takingAmount);
                                                          }
                                                      }
                                              
                                                      if (makingAmount == 0 || takingAmount == 0) revert RFQSwapWithZeroAmount();
                                              
                                                      // Maker => Taker
                                                      if (order.makerAsset == address(_WETH) && flagsAndAmount & _UNWRAP_WETH_FLAG != 0) {
                                                          _WETH.transferFrom(maker, address(this), makingAmount);
                                                          _WETH.withdraw(makingAmount);
                                                          // solhint-disable-next-line avoid-low-level-calls
                                                          (bool success, ) = target.call{value: makingAmount, gas: _RAW_CALL_GAS_LIMIT}("");
                                                          if (!success) revert Errors.ETHTransferFailed();
                                                      } else {
                                                          IERC20(order.makerAsset).safeTransferFrom(maker, target, makingAmount);
                                                      }
                                              
                                                      // Taker => Maker
                                                      if (order.takerAsset == address(_WETH) && msg.value > 0) {
                                                          if (msg.value != takingAmount) revert Errors.InvalidMsgValue();
                                                          _WETH.deposit{ value: takingAmount }();
                                                          _WETH.transfer(maker, takingAmount);
                                                      } else {
                                                          if (msg.value != 0) revert Errors.InvalidMsgValue();
                                                          IERC20(order.takerAsset).safeTransferFrom(msg.sender, maker, takingAmount);
                                                      }
                                                  }
                                              
                                                  function _invalidateOrder(address maker, uint256 orderInfo, uint256 additionalMask) private {
                                                      uint256 invalidatorSlot = uint64(orderInfo) >> 8;
                                                      uint256 invalidatorBits = (1 << uint8(orderInfo)) | additionalMask;
                                                      mapping(uint256 => uint256) storage invalidatorStorage = _invalidator[maker];
                                                      uint256 invalidator = invalidatorStorage[invalidatorSlot];
                                                      if (invalidator & invalidatorBits == invalidatorBits) revert InvalidatedOrder();
                                                      invalidatorStorage[invalidatorSlot] = invalidator | invalidatorBits;
                                                  }
                                              }
                                              
                                              
                                              // File @1inch/limit-order-protocol/contracts/[email protected]
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              library OrderLib {
                                                  struct Order {
                                                      uint256 salt;
                                                      address makerAsset;
                                                      address takerAsset;
                                                      address maker;
                                                      address receiver;
                                                      address allowedSender;  // equals to Zero address on public orders
                                                      uint256 makingAmount;
                                                      uint256 takingAmount;
                                                      uint256 offsets;
                                                      // bytes makerAssetData;
                                                      // bytes takerAssetData;
                                                      // bytes getMakingAmount; // this.staticcall(abi.encodePacked(bytes, swapTakerAmount)) => (swapMakerAmount)
                                                      // bytes getTakingAmount; // this.staticcall(abi.encodePacked(bytes, swapMakerAmount)) => (swapTakerAmount)
                                                      // bytes predicate;       // this.staticcall(bytes) => (bool)
                                                      // bytes permit;          // On first fill: permit.1.call(abi.encodePacked(permit.selector, permit.2))
                                                      // bytes preInteraction;
                                                      // bytes postInteraction;
                                                      bytes interactions; // concat(makerAssetData, takerAssetData, getMakingAmount, getTakingAmount, predicate, permit, preIntercation, postInteraction)
                                                  }
                                              
                                                  bytes32 constant internal _LIMIT_ORDER_TYPEHASH = keccak256(
                                                      "Order("
                                                          "uint256 salt,"
                                                          "address makerAsset,"
                                                          "address takerAsset,"
                                                          "address maker,"
                                                          "address receiver,"
                                                          "address allowedSender,"
                                                          "uint256 makingAmount,"
                                                          "uint256 takingAmount,"
                                                          "uint256 offsets,"
                                                          "bytes interactions"
                                                      ")"
                                                  );
                                              
                                                  enum DynamicField {
                                                      MakerAssetData,
                                                      TakerAssetData,
                                                      GetMakingAmount,
                                                      GetTakingAmount,
                                                      Predicate,
                                                      Permit,
                                                      PreInteraction,
                                                      PostInteraction
                                                  }
                                              
                                                  function getterIsFrozen(bytes calldata getter) internal pure returns(bool) {
                                                      return getter.length == 1 && getter[0] == "x";
                                                  }
                                              
                                                  function _get(Order calldata order, DynamicField field) private pure returns(bytes calldata) {
                                                      uint256 bitShift = uint256(field) << 5; // field * 32
                                                      return order.interactions[
                                                          uint32((order.offsets << 32) >> bitShift):
                                                          uint32(order.offsets >> bitShift)
                                                      ];
                                                  }
                                              
                                                  function makerAssetData(Order calldata order) internal pure returns(bytes calldata) {
                                                      return _get(order, DynamicField.MakerAssetData);
                                                  }
                                              
                                                  function takerAssetData(Order calldata order) internal pure returns(bytes calldata) {
                                                      return _get(order, DynamicField.TakerAssetData);
                                                  }
                                              
                                                  function getMakingAmount(Order calldata order) internal pure returns(bytes calldata) {
                                                      return _get(order, DynamicField.GetMakingAmount);
                                                  }
                                              
                                                  function getTakingAmount(Order calldata order) internal pure returns(bytes calldata) {
                                                      return _get(order, DynamicField.GetTakingAmount);
                                                  }
                                              
                                                  function predicate(Order calldata order) internal pure returns(bytes calldata) {
                                                      return _get(order, DynamicField.Predicate);
                                                  }
                                              
                                                  function permit(Order calldata order) internal pure returns(bytes calldata) {
                                                      return _get(order, DynamicField.Permit);
                                                  }
                                              
                                                  function preInteraction(Order calldata order) internal pure returns(bytes calldata) {
                                                      return _get(order, DynamicField.PreInteraction);
                                                  }
                                              
                                                  function postInteraction(Order calldata order) internal pure returns(bytes calldata) {
                                                      return _get(order, DynamicField.PostInteraction);
                                                  }
                                              
                                                  function hash(Order calldata order, bytes32 domainSeparator) internal pure returns(bytes32 result) {
                                                      bytes calldata interactions = order.interactions;
                                                      bytes32 typehash = _LIMIT_ORDER_TYPEHASH;
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let ptr := mload(0x40)
                                              
                                                          // keccak256(abi.encode(_LIMIT_ORDER_TYPEHASH, orderWithoutInteractions, keccak256(order.interactions)));
                                                          calldatacopy(ptr, interactions.offset, interactions.length)
                                                          mstore(add(ptr, 0x140), keccak256(ptr, interactions.length))
                                                          calldatacopy(add(ptr, 0x20), order, 0x120)
                                                          mstore(ptr, typehash)
                                                          result := keccak256(ptr, 0x160)
                                                      }
                                                      result = ECDSA.toTypedDataHash(domainSeparator, result);
                                                  }
                                              }
                                              
                                              
                                              // File @1inch/limit-order-protocol/contracts/libraries/[email protected]
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              /// @title Library with gas efficient alternatives to `abi.decode`
                                              library ArgumentsDecoder {
                                                  error IncorrectDataLength();
                                              
                                                  function decodeUint256(bytes calldata data, uint256 offset) internal pure returns(uint256 value) {
                                                      unchecked { if (data.length < offset + 32) revert IncorrectDataLength(); }
                                                      // no memory ops inside so this insertion is automatically memory safe
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          value := calldataload(add(data.offset, offset))
                                                      }
                                                  }
                                              
                                                  function decodeSelector(bytes calldata data) internal pure returns(bytes4 value) {
                                                      if (data.length < 4) revert IncorrectDataLength();
                                                      // no memory ops inside so this insertion is automatically memory safe
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          value := calldataload(data.offset)
                                                      }
                                                  }
                                              
                                                  function decodeTailCalldata(bytes calldata data, uint256 tailOffset) internal pure returns(bytes calldata args) {
                                                      if (data.length < tailOffset) revert IncorrectDataLength();
                                                      // no memory ops inside so this insertion is automatically memory safe
                                                      assembly {  // solhint-disable-line no-inline-assembly
                                                          args.offset := add(data.offset, tailOffset)
                                                          args.length := sub(data.length, tailOffset)
                                                      }
                                                  }
                                              
                                                  function decodeTargetAndCalldata(bytes calldata data) internal pure returns(address target, bytes calldata args) {
                                                      if (data.length < 20) revert IncorrectDataLength();
                                                      // no memory ops inside so this insertion is automatically memory safe
                                                      assembly {  // solhint-disable-line no-inline-assembly
                                                          target := shr(96, calldataload(data.offset))
                                                          args.offset := add(data.offset, 20)
                                                          args.length := sub(data.length, 20)
                                                      }
                                                  }
                                              }
                                              
                                              
                                              // File @1inch/limit-order-protocol/contracts/helpers/[email protected]
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              /// @title A helper contract for managing nonce of tx sender
                                              contract NonceManager {
                                                  error AdvanceNonceFailed();
                                                  event NonceIncreased(address indexed maker, uint256 newNonce);
                                              
                                                  mapping(address => uint256) public nonce;
                                              
                                                  /// @notice Advances nonce by one
                                                  function increaseNonce() external {
                                                      advanceNonce(1);
                                                  }
                                              
                                                  /// @notice Advances nonce by specified amount
                                                  function advanceNonce(uint8 amount) public {
                                                      if (amount == 0) revert AdvanceNonceFailed();
                                                      uint256 newNonce = nonce[msg.sender] + amount;
                                                      nonce[msg.sender] = newNonce;
                                                      emit NonceIncreased(msg.sender, newNonce);
                                                  }
                                              
                                                  /// @notice Checks if `makerAddress` has specified `makerNonce`
                                                  /// @return Result True if `makerAddress` has specified nonce. Otherwise, false
                                                  function nonceEquals(address makerAddress, uint256 makerNonce) public view returns(bool) {
                                                      return nonce[makerAddress] == makerNonce;
                                                  }
                                              }
                                              
                                              
                                              // File @1inch/limit-order-protocol/contracts/helpers/[email protected]
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              
                                              /// @title A helper contract for executing boolean functions on arbitrary target call results
                                              contract PredicateHelper is NonceManager {
                                                  using ArgumentsDecoder for bytes;
                                              
                                                  error ArbitraryStaticCallFailed();
                                              
                                                  /// @notice Calls every target with corresponding data
                                                  /// @return Result True if call to any target returned True. Otherwise, false
                                                  function or(uint256 offsets, bytes calldata data) public view returns(bool) {
                                                      uint256 current;
                                                      uint256 previous;
                                                      for (uint256 i = 0; (current = uint32(offsets >> i)) != 0; i += 32) {
                                                          (bool success, uint256 res) = _selfStaticCall(data[previous:current]);
                                                          if (success && res == 1) {
                                                              return true;
                                                          }
                                                          previous = current;
                                                      }
                                                      return false;
                                                  }
                                              
                                                  /// @notice Calls every target with corresponding data
                                                  /// @return Result True if calls to all targets returned True. Otherwise, false
                                                  function and(uint256 offsets, bytes calldata data) public view returns(bool) {
                                                      uint256 current;
                                                      uint256 previous;
                                                      for (uint256 i = 0; (current = uint32(offsets >> i)) != 0; i += 32) {
                                                          (bool success, uint256 res) = _selfStaticCall(data[previous:current]);
                                                          if (!success || res != 1) {
                                                              return false;
                                                          }
                                                          previous = current;
                                                      }
                                                      return true;
                                                  }
                                              
                                                  /// @notice Calls target with specified data and tests if it's equal to the value
                                                  /// @param value Value to test
                                                  /// @return Result True if call to target returns the same value as `value`. Otherwise, false
                                                  function eq(uint256 value, bytes calldata data) public view returns(bool) {
                                                      (bool success, uint256 res) = _selfStaticCall(data);
                                                      return success && res == value;
                                                  }
                                              
                                                  /// @notice Calls target with specified data and tests if it's lower than value
                                                  /// @param value Value to test
                                                  /// @return Result True if call to target returns value which is lower than `value`. Otherwise, false
                                                  function lt(uint256 value, bytes calldata data) public view returns(bool) {
                                                      (bool success, uint256 res) = _selfStaticCall(data);
                                                      return success && res < value;
                                                  }
                                              
                                                  /// @notice Calls target with specified data and tests if it's bigger than value
                                                  /// @param value Value to test
                                                  /// @return Result True if call to target returns value which is bigger than `value`. Otherwise, false
                                                  function gt(uint256 value, bytes calldata data) public view returns(bool) {
                                                      (bool success, uint256 res) = _selfStaticCall(data);
                                                      return success && res > value;
                                                  }
                                              
                                                  /// @notice Checks passed time against block timestamp
                                                  /// @return Result True if current block timestamp is lower than `time`. Otherwise, false
                                                  function timestampBelow(uint256 time) public view returns(bool) {
                                                      return block.timestamp < time;  // solhint-disable-line not-rely-on-time
                                                  }
                                              
                                                  /// @notice Performs an arbitrary call to target with data
                                                  /// @return Result Bytes transmuted to uint256
                                                  function arbitraryStaticCall(address target, bytes calldata data) public view returns(uint256) {
                                                      (bool success, uint256 res) = _staticcallForUint(target, data);
                                                      if (!success) revert ArbitraryStaticCallFailed();
                                                      return res;
                                                  }
                                              
                                                  function timestampBelowAndNonceEquals(uint256 timeNonceAccount) public view returns(bool) {
                                                      uint256 _time = uint48(timeNonceAccount >> 208);
                                                      uint256 _nonce = uint48(timeNonceAccount >> 160);
                                                      address _account = address(uint160(timeNonceAccount));
                                                      return timestampBelow(_time) && nonceEquals(_account, _nonce);
                                                  }
                                              
                                                  function _selfStaticCall(bytes calldata data) internal view returns(bool, uint256) {
                                                      uint256 selector = uint32(data.decodeSelector());
                                                      uint256 arg = data.decodeUint256(4);
                                              
                                                      // special case for the most often used predicate
                                                      if (selector == uint32(this.timestampBelowAndNonceEquals.selector)) {  // 0x2cc2878d
                                                          return (true, timestampBelowAndNonceEquals(arg) ? 1 : 0);
                                                      }
                                              
                                                      if (selector < uint32(this.arbitraryStaticCall.selector)) {  // 0xbf15fcd8
                                                          if (selector < uint32(this.eq.selector)) {  // 0x6fe7b0ba
                                                              if (selector == uint32(this.gt.selector)) {  // 0x4f38e2b8
                                                                  return (true, gt(arg, data.decodeTailCalldata(100)) ? 1 : 0);
                                                              } else if (selector == uint32(this.timestampBelow.selector)) {  // 0x63592c2b
                                                                  return (true, timestampBelow(arg) ? 1 : 0);
                                                              }
                                                          } else {
                                                              if (selector == uint32(this.eq.selector)) {  // 0x6fe7b0ba
                                                                  return (true, eq(arg, data.decodeTailCalldata(100)) ? 1 : 0);
                                                              } else if (selector == uint32(this.or.selector)) {  // 0x74261145
                                                                  return (true, or(arg, data.decodeTailCalldata(100)) ? 1 : 0);
                                                              }
                                                          }
                                                      } else {
                                                          if (selector < uint32(this.lt.selector)) {  // 0xca4ece22
                                                              if (selector == uint32(this.arbitraryStaticCall.selector)) {  // 0xbf15fcd8
                                                                  return (true, arbitraryStaticCall(address(uint160(arg)), data.decodeTailCalldata(100)));
                                                              } else if (selector == uint32(this.and.selector)) {  // 0xbfa75143
                                                                  return (true, and(arg, data.decodeTailCalldata(100)) ? 1 : 0);
                                                              }
                                                          } else {
                                                              if (selector == uint32(this.lt.selector)) {  // 0xca4ece22
                                                                  return (true, lt(arg, data.decodeTailCalldata(100)) ? 1 : 0);
                                                              } else if (selector == uint32(this.nonceEquals.selector)) {  // 0xcf6fc6e3
                                                                  return (true, nonceEquals(address(uint160(arg)), data.decodeUint256(0x24)) ? 1 : 0);
                                                              }
                                                          }
                                                      }
                                              
                                                      return _staticcallForUint(address(this), data);
                                                  }
                                              
                                                  function _staticcallForUint(address target, bytes calldata input) private view returns(bool success, uint256 res) {
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let data := mload(0x40)
                                              
                                                          calldatacopy(data, input.offset, input.length)
                                                          success := staticcall(gas(), target, data, input.length, 0x0, 0x20)
                                                          success := and(success, eq(returndatasize(), 32))
                                                          if success {
                                                              res := mload(0)
                                                          }
                                                      }
                                                  }
                                              }
                                              
                                              
                                              // File @1inch/limit-order-protocol/contracts/interfaces/[email protected]
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              interface IOrderMixin {
                                                  /**
                                                   * @notice Returns unfilled amount for order. Throws if order does not exist
                                                   * @param orderHash Order's hash. Can be obtained by the `hashOrder` function
                                                   * @return amount Unfilled amount
                                                   */
                                                  function remaining(bytes32 orderHash) external view returns(uint256 amount);
                                              
                                                  /**
                                                   * @notice Returns unfilled amount for order
                                                   * @param orderHash Order's hash. Can be obtained by the `hashOrder` function
                                                   * @return rawAmount Unfilled amount of order plus one if order exists. Otherwise 0
                                                   */
                                                  function remainingRaw(bytes32 orderHash) external view returns(uint256 rawAmount);
                                              
                                                  /**
                                                   * @notice Same as `remainingRaw` but for multiple orders
                                                   * @param orderHashes Array of hashes
                                                   * @return rawAmounts Array of amounts for each order plus one if order exists or 0 otherwise
                                                   */
                                                  function remainingsRaw(bytes32[] memory orderHashes) external view returns(uint256[] memory rawAmounts);
                                              
                                                  /**
                                                   * @notice Checks order predicate
                                                   * @param order Order to check predicate for
                                                   * @return result Predicate evaluation result. True if predicate allows to fill the order, false otherwise
                                                   */
                                                  function checkPredicate(OrderLib.Order calldata order) external view returns(bool result);
                                              
                                                  /**
                                                   * @notice Returns order hash according to EIP712 standard
                                                   * @param order Order to get hash for
                                                   * @return orderHash Hash of the order
                                                   */
                                                  function hashOrder(OrderLib.Order calldata order) external view returns(bytes32);
                                              
                                                  /**
                                                   * @notice Delegates execution to custom implementation. Could be used to validate if `transferFrom` works properly
                                                   * @dev The function always reverts and returns the simulation results in revert data.
                                                   * @param target Addresses that will be delegated
                                                   * @param data Data that will be passed to delegatee
                                                   */
                                                  function simulate(address target, bytes calldata data) external;
                                              
                                                  /**
                                                   * @notice Cancels order.
                                                   * @dev Order is cancelled by setting remaining amount to _ORDER_FILLED value
                                                   * @param order Order quote to cancel
                                                   * @return orderRemaining Unfilled amount of order before cancellation
                                                   * @return orderHash Hash of the filled order
                                                   */
                                                  function cancelOrder(OrderLib.Order calldata order) external returns(uint256 orderRemaining, bytes32 orderHash);
                                              
                                                  /**
                                                   * @notice Fills an order. If one doesn't exist (first fill) it will be created using order.makerAssetData
                                                   * @param order Order quote to fill
                                                   * @param signature Signature to confirm quote ownership
                                                   * @param interaction A call data for InteractiveNotificationReceiver. Taker may execute interaction after getting maker assets and before sending taker assets.
                                                   * @param makingAmount Making amount
                                                   * @param takingAmount Taking amount
                                                   * @param skipPermitAndThresholdAmount Specifies maximum allowed takingAmount when takingAmount is zero, otherwise specifies minimum allowed makingAmount. Top-most bit specifies whether taker wants to skip maker's permit.
                                                   * @return actualMakingAmount Actual amount transferred from maker to taker
                                                   * @return actualTakingAmount Actual amount transferred from taker to maker
                                                   * @return orderHash Hash of the filled order
                                                   */
                                                  function fillOrder(
                                                      OrderLib.Order calldata order,
                                                      bytes calldata signature,
                                                      bytes calldata interaction,
                                                      uint256 makingAmount,
                                                      uint256 takingAmount,
                                                      uint256 skipPermitAndThresholdAmount
                                                  ) external payable returns(uint256 actualMakingAmount, uint256 actualTakingAmount, bytes32 orderHash);
                                              
                                                  /**
                                                   * @notice Same as `fillOrderTo` but calls permit first,
                                                   * allowing to approve token spending and make a swap in one transaction.
                                                   * Also allows to specify funds destination instead of `msg.sender`
                                                   * @dev See tests for examples
                                                   * @param order Order quote to fill
                                                   * @param signature Signature to confirm quote ownership
                                                   * @param interaction A call data for InteractiveNotificationReceiver. Taker may execute interaction after getting maker assets and before sending taker assets.
                                                   * @param makingAmount Making amount
                                                   * @param takingAmount Taking amount
                                                   * @param skipPermitAndThresholdAmount Specifies maximum allowed takingAmount when takingAmount is zero, otherwise specifies minimum allowed makingAmount. Top-most bit specifies whether taker wants to skip maker's permit.
                                                   * @param target Address that will receive swap funds
                                                   * @param permit Should consist of abiencoded token address and encoded `IERC20Permit.permit` call.
                                                   * @return actualMakingAmount Actual amount transferred from maker to taker
                                                   * @return actualTakingAmount Actual amount transferred from taker to maker
                                                   * @return orderHash Hash of the filled order
                                                   */
                                                  function fillOrderToWithPermit(
                                                      OrderLib.Order calldata order,
                                                      bytes calldata signature,
                                                      bytes calldata interaction,
                                                      uint256 makingAmount,
                                                      uint256 takingAmount,
                                                      uint256 skipPermitAndThresholdAmount,
                                                      address target,
                                                      bytes calldata permit
                                                  ) external returns(uint256 actualMakingAmount, uint256 actualTakingAmount, bytes32 orderHash);
                                              
                                                  /**
                                                   * @notice Same as `fillOrder` but allows to specify funds destination instead of `msg.sender`
                                                   * @param order_ Order quote to fill
                                                   * @param signature Signature to confirm quote ownership
                                                   * @param interaction A call data for InteractiveNotificationReceiver. Taker may execute interaction after getting maker assets and before sending taker assets.
                                                   * @param makingAmount Making amount
                                                   * @param takingAmount Taking amount
                                                   * @param skipPermitAndThresholdAmount Specifies maximum allowed takingAmount when takingAmount is zero, otherwise specifies minimum allowed makingAmount. Top-most bit specifies whether taker wants to skip maker's permit.
                                                   * @param target Address that will receive swap funds
                                                   * @return actualMakingAmount Actual amount transferred from maker to taker
                                                   * @return actualTakingAmount Actual amount transferred from taker to maker
                                                   * @return orderHash Hash of the filled order
                                                   */
                                                  function fillOrderTo(
                                                      OrderLib.Order calldata order_,
                                                      bytes calldata signature,
                                                      bytes calldata interaction,
                                                      uint256 makingAmount,
                                                      uint256 takingAmount,
                                                      uint256 skipPermitAndThresholdAmount,
                                                      address target
                                                  ) external payable returns(uint256 actualMakingAmount, uint256 actualTakingAmount, bytes32 orderHash);
                                              }
                                              
                                              
                                              // File @1inch/limit-order-protocol/contracts/interfaces/[email protected]
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              /// @title Interface for interactor which acts between `maker => taker` and `taker => maker` transfers.
                                              interface PreInteractionNotificationReceiver {
                                                  function fillOrderPreInteraction(
                                                      bytes32 orderHash,
                                                      address maker,
                                                      address taker,
                                                      uint256 makingAmount,
                                                      uint256 takingAmount,
                                                      uint256 remainingAmount,
                                                      bytes memory interactiveData
                                                  ) external;
                                              }
                                              
                                              interface PostInteractionNotificationReceiver {
                                                  /// @notice Callback method that gets called after taker transferred funds to maker but before
                                                  /// the opposite transfer happened
                                                  function fillOrderPostInteraction(
                                                      bytes32 orderHash,
                                                      address maker,
                                                      address taker,
                                                      uint256 makingAmount,
                                                      uint256 takingAmount,
                                                      uint256 remainingAmount,
                                                      bytes memory interactiveData
                                                  ) external;
                                              }
                                              
                                              interface InteractionNotificationReceiver {
                                                  function fillOrderInteraction(
                                                      address taker,
                                                      uint256 makingAmount,
                                                      uint256 takingAmount,
                                                      bytes memory interactiveData
                                                  ) external returns(uint256 offeredTakingAmount);
                                              }
                                              
                                              
                                              // File @1inch/limit-order-protocol/contracts/[email protected]
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              /// @title Regular Limit Order mixin
                                              abstract contract OrderMixin is IOrderMixin, EIP712, PredicateHelper {
                                                  using SafeERC20 for IERC20;
                                                  using ArgumentsDecoder for bytes;
                                                  using OrderLib for OrderLib.Order;
                                              
                                                  error UnknownOrder();
                                                  error AccessDenied();
                                                  error AlreadyFilled();
                                                  error PermitLengthTooLow();
                                                  error ZeroTargetIsForbidden();
                                                  error RemainingAmountIsZero();
                                                  error PrivateOrder();
                                                  error BadSignature();
                                                  error ReentrancyDetected();
                                                  error PredicateIsNotTrue();
                                                  error OnlyOneAmountShouldBeZero();
                                                  error TakingAmountTooHigh();
                                                  error MakingAmountTooLow();
                                                  error SwapWithZeroAmount();
                                                  error TransferFromMakerToTakerFailed();
                                                  error TransferFromTakerToMakerFailed();
                                                  error WrongAmount();
                                                  error WrongGetter();
                                                  error GetAmountCallFailed();
                                                  error TakingAmountIncreased();
                                                  error SimulationResults(bool success, bytes res);
                                              
                                                  /// @notice Emitted every time order gets filled, including partial fills
                                                  event OrderFilled(
                                                      address indexed maker,
                                                      bytes32 orderHash,
                                                      uint256 remaining
                                                  );
                                              
                                                  /// @notice Emitted when order gets cancelled
                                                  event OrderCanceled(
                                                      address indexed maker,
                                                      bytes32 orderHash,
                                                      uint256 remainingRaw
                                                  );
                                              
                                                  uint256 constant private _ORDER_DOES_NOT_EXIST = 0;
                                                  uint256 constant private _ORDER_FILLED = 1;
                                                  uint256 constant private _SKIP_PERMIT_FLAG = 1 << 255;
                                                  uint256 constant private _THRESHOLD_MASK = ~_SKIP_PERMIT_FLAG;
                                              
                                                  IWETH private immutable _WETH;  // solhint-disable-line var-name-mixedcase
                                                  /// @notice Stores unfilled amounts for each order plus one.
                                                  /// Therefore 0 means order doesn't exist and 1 means order was filled
                                                  mapping(bytes32 => uint256) private _remaining;
                                              
                                                  constructor(IWETH weth) {
                                                      _WETH = weth;
                                                  }
                                              
                                                  /**
                                                   * @notice See {IOrderMixin-remaining}.
                                                   */
                                                  function remaining(bytes32 orderHash) external view returns(uint256 /* amount */) {
                                                      uint256 amount = _remaining[orderHash];
                                                      if (amount == _ORDER_DOES_NOT_EXIST) revert UnknownOrder();
                                                      unchecked { return amount - 1; }
                                                  }
                                              
                                                  /**
                                                   * @notice See {IOrderMixin-remainingRaw}.
                                                   */
                                                  function remainingRaw(bytes32 orderHash) external view returns(uint256 /* rawAmount */) {
                                                      return _remaining[orderHash];
                                                  }
                                              
                                                  /**
                                                   * @notice See {IOrderMixin-remainingsRaw}.
                                                   */
                                                  function remainingsRaw(bytes32[] memory orderHashes) external view returns(uint256[] memory /* rawAmounts */) {
                                                      uint256[] memory results = new uint256[](orderHashes.length);
                                                      for (uint256 i = 0; i < orderHashes.length; i++) {
                                                          results[i] = _remaining[orderHashes[i]];
                                                      }
                                                      return results;
                                                  }
                                              
                                                  /**
                                                   * @notice See {IOrderMixin-simulate}.
                                                   */
                                                  function simulate(address target, bytes calldata data) external {
                                                      // solhint-disable-next-line avoid-low-level-calls
                                                      (bool success, bytes memory result) = target.delegatecall(data);
                                                      revert SimulationResults(success, result);
                                                  }
                                              
                                                  /**
                                                   * @notice See {IOrderMixin-cancelOrder}.
                                                   */
                                                  function cancelOrder(OrderLib.Order calldata order) external returns(uint256 orderRemaining, bytes32 orderHash) {
                                                      if (order.maker != msg.sender) revert AccessDenied();
                                              
                                                      orderHash = hashOrder(order);
                                                      orderRemaining = _remaining[orderHash];
                                                      if (orderRemaining == _ORDER_FILLED) revert AlreadyFilled();
                                                      emit OrderCanceled(msg.sender, orderHash, orderRemaining);
                                                      _remaining[orderHash] = _ORDER_FILLED;
                                                  }
                                              
                                                  /**
                                                   * @notice See {IOrderMixin-fillOrder}.
                                                   */
                                                  function fillOrder(
                                                      OrderLib.Order calldata order,
                                                      bytes calldata signature,
                                                      bytes calldata interaction,
                                                      uint256 makingAmount,
                                                      uint256 takingAmount,
                                                      uint256 skipPermitAndThresholdAmount
                                                  ) external payable returns(uint256 /* actualMakingAmount */, uint256 /* actualTakingAmount */, bytes32 /* orderHash */) {
                                                      return fillOrderTo(order, signature, interaction, makingAmount, takingAmount, skipPermitAndThresholdAmount, msg.sender);
                                                  }
                                              
                                                  /**
                                                   * @notice See {IOrderMixin-fillOrderToWithPermit}.
                                                   */
                                                  function fillOrderToWithPermit(
                                                      OrderLib.Order calldata order,
                                                      bytes calldata signature,
                                                      bytes calldata interaction,
                                                      uint256 makingAmount,
                                                      uint256 takingAmount,
                                                      uint256 skipPermitAndThresholdAmount,
                                                      address target,
                                                      bytes calldata permit
                                                  ) external returns(uint256 /* actualMakingAmount */, uint256 /* actualTakingAmount */, bytes32 /* orderHash */) {
                                                      if (permit.length < 20) revert PermitLengthTooLow();
                                                      {  // Stack too deep
                                                          (address token, bytes calldata permitData) = permit.decodeTargetAndCalldata();
                                                          IERC20(token).safePermit(permitData);
                                                      }
                                                      return fillOrderTo(order, signature, interaction, makingAmount, takingAmount, skipPermitAndThresholdAmount, target);
                                                  }
                                              
                                                  /**
                                                   * @notice See {IOrderMixin-fillOrderTo}.
                                                   */
                                                  function fillOrderTo(
                                                      OrderLib.Order calldata order_,
                                                      bytes calldata signature,
                                                      bytes calldata interaction,
                                                      uint256 makingAmount,
                                                      uint256 takingAmount,
                                                      uint256 skipPermitAndThresholdAmount,
                                                      address target
                                                  ) public payable returns(uint256 actualMakingAmount, uint256 actualTakingAmount, bytes32 orderHash) {
                                                      if (target == address(0)) revert ZeroTargetIsForbidden();
                                                      orderHash = hashOrder(order_);
                                              
                                                      OrderLib.Order calldata order = order_; // Helps with "Stack too deep"
                                                      actualMakingAmount = makingAmount;
                                                      actualTakingAmount = takingAmount;
                                              
                                                      uint256 remainingMakingAmount = _remaining[orderHash];
                                                      if (remainingMakingAmount == _ORDER_FILLED) revert RemainingAmountIsZero();
                                                      if (order.allowedSender != address(0) && order.allowedSender != msg.sender) revert PrivateOrder();
                                                      if (remainingMakingAmount == _ORDER_DOES_NOT_EXIST) {
                                                          // First fill: validate order and permit maker asset
                                                          if (!ECDSA.recoverOrIsValidSignature(order.maker, orderHash, signature)) revert BadSignature();
                                                          remainingMakingAmount = order.makingAmount;
                                              
                                                          bytes calldata permit = order.permit();
                                                          if (skipPermitAndThresholdAmount & _SKIP_PERMIT_FLAG == 0 && permit.length >= 20) {
                                                              // proceed only if taker is willing to execute permit and its length is enough to store address
                                                              (address token, bytes calldata permitCalldata) = permit.decodeTargetAndCalldata();
                                                              IERC20(token).safePermit(permitCalldata);
                                                              if (_remaining[orderHash] != _ORDER_DOES_NOT_EXIST) revert ReentrancyDetected();
                                                          }
                                                      } else {
                                                          unchecked { remainingMakingAmount -= 1; }
                                                      }
                                              
                                                      // Check if order is valid
                                                      if (order.predicate().length > 0) {
                                                          if (!checkPredicate(order)) revert PredicateIsNotTrue();
                                                      }
                                              
                                                      // Compute maker and taker assets amount
                                                      if ((actualTakingAmount == 0) == (actualMakingAmount == 0)) {
                                                          revert OnlyOneAmountShouldBeZero();
                                                      } else if (actualTakingAmount == 0) {
                                                          if (actualMakingAmount > remainingMakingAmount) {
                                                              actualMakingAmount = remainingMakingAmount;
                                                          }
                                                          actualTakingAmount = _getTakingAmount(order.getTakingAmount(), order.makingAmount, actualMakingAmount, order.takingAmount, remainingMakingAmount, orderHash);
                                                          uint256 thresholdAmount = skipPermitAndThresholdAmount & _THRESHOLD_MASK;
                                                          // check that actual rate is not worse than what was expected
                                                          // actualTakingAmount / actualMakingAmount <= thresholdAmount / makingAmount
                                                          if (actualTakingAmount * makingAmount > thresholdAmount * actualMakingAmount) revert TakingAmountTooHigh();
                                                      } else {
                                                          actualMakingAmount = _getMakingAmount(order.getMakingAmount(), order.takingAmount, actualTakingAmount, order.makingAmount, remainingMakingAmount, orderHash);
                                                          if (actualMakingAmount > remainingMakingAmount) {
                                                              actualMakingAmount = remainingMakingAmount;
                                                              actualTakingAmount = _getTakingAmount(order.getTakingAmount(), order.makingAmount, actualMakingAmount, order.takingAmount, remainingMakingAmount, orderHash);
                                                              if (actualTakingAmount > takingAmount) revert TakingAmountIncreased();
                                                          }
                                                          uint256 thresholdAmount = skipPermitAndThresholdAmount & _THRESHOLD_MASK;
                                                          // check that actual rate is not worse than what was expected
                                                          // actualMakingAmount / actualTakingAmount >= thresholdAmount / takingAmount
                                                          if (actualMakingAmount * takingAmount < thresholdAmount * actualTakingAmount) revert MakingAmountTooLow();
                                                      }
                                              
                                                      if (actualMakingAmount == 0 || actualTakingAmount == 0) revert SwapWithZeroAmount();
                                              
                                                      // Update remaining amount in storage
                                                      unchecked {
                                                          remainingMakingAmount = remainingMakingAmount - actualMakingAmount;
                                                          _remaining[orderHash] = remainingMakingAmount + 1;
                                                      }
                                                      emit OrderFilled(order_.maker, orderHash, remainingMakingAmount);
                                              
                                                      // Maker can handle funds interactively
                                                      if (order.preInteraction().length >= 20) {
                                                          // proceed only if interaction length is enough to store address
                                                          (address interactionTarget, bytes calldata interactionData) = order.preInteraction().decodeTargetAndCalldata();
                                                          PreInteractionNotificationReceiver(interactionTarget).fillOrderPreInteraction(
                                                              orderHash, order.maker, msg.sender, actualMakingAmount, actualTakingAmount, remainingMakingAmount, interactionData
                                                          );
                                                      }
                                              
                                                      // Maker => Taker
                                                      if (!_callTransferFrom(
                                                          order.makerAsset,
                                                          order.maker,
                                                          target,
                                                          actualMakingAmount,
                                                          order.makerAssetData()
                                                      )) revert TransferFromMakerToTakerFailed();
                                              
                                                      if (interaction.length >= 20) {
                                                          // proceed only if interaction length is enough to store address
                                                          (address interactionTarget, bytes calldata interactionData) = interaction.decodeTargetAndCalldata();
                                                          uint256 offeredTakingAmount = InteractionNotificationReceiver(interactionTarget).fillOrderInteraction(
                                                              msg.sender, actualMakingAmount, actualTakingAmount, interactionData
                                                          );
                                              
                                                          if (offeredTakingAmount > actualTakingAmount &&
                                                              !OrderLib.getterIsFrozen(order.getMakingAmount()) &&
                                                              !OrderLib.getterIsFrozen(order.getTakingAmount()))
                                                          {
                                                              actualTakingAmount = offeredTakingAmount;
                                                          }
                                                      }
                                              
                                                      // Taker => Maker
                                                      if (order.takerAsset == address(_WETH) && msg.value > 0) {
                                                          if (msg.value < actualTakingAmount) revert Errors.InvalidMsgValue();
                                                          if (msg.value > actualTakingAmount) {
                                                              unchecked {
                                                                  (bool success, ) = msg.sender.call{value: msg.value - actualTakingAmount}("");  // solhint-disable-line avoid-low-level-calls
                                                                  if (!success) revert Errors.ETHTransferFailed();
                                                              }
                                                          }
                                                          _WETH.deposit{ value: actualTakingAmount }();
                                                          _WETH.transfer(order.receiver == address(0) ? order.maker : order.receiver, actualTakingAmount);
                                                      } else {
                                                          if (msg.value != 0) revert Errors.InvalidMsgValue();
                                                          if (!_callTransferFrom(
                                                              order.takerAsset,
                                                              msg.sender,
                                                              order.receiver == address(0) ? order.maker : order.receiver,
                                                              actualTakingAmount,
                                                              order.takerAssetData()
                                                          )) revert TransferFromTakerToMakerFailed();
                                                      }
                                              
                                                      // Maker can handle funds interactively
                                                      if (order.postInteraction().length >= 20) {
                                                          // proceed only if interaction length is enough to store address
                                                          (address interactionTarget, bytes calldata interactionData) = order.postInteraction().decodeTargetAndCalldata();
                                                          PostInteractionNotificationReceiver(interactionTarget).fillOrderPostInteraction(
                                                               orderHash, order.maker, msg.sender, actualMakingAmount, actualTakingAmount, remainingMakingAmount, interactionData
                                                          );
                                                      }
                                                  }
                                              
                                                  /**
                                                   * @notice See {IOrderMixin-checkPredicate}.
                                                   */
                                                  function checkPredicate(OrderLib.Order calldata order) public view returns(bool) {
                                                      (bool success, uint256 res) = _selfStaticCall(order.predicate());
                                                      return success && res == 1;
                                                  }
                                              
                                                  /**
                                                   * @notice See {IOrderMixin-hashOrder}.
                                                   */
                                                  function hashOrder(OrderLib.Order calldata order) public view returns(bytes32) {
                                                      return order.hash(_domainSeparatorV4());
                                                  }
                                              
                                                  function _callTransferFrom(address asset, address from, address to, uint256 amount, bytes calldata input) private returns(bool success) {
                                                      bytes4 selector = IERC20.transferFrom.selector;
                                                      /// @solidity memory-safe-assembly
                                                      assembly { // solhint-disable-line no-inline-assembly
                                                          let data := mload(0x40)
                                              
                                                          mstore(data, selector)
                                                          mstore(add(data, 0x04), from)
                                                          mstore(add(data, 0x24), to)
                                                          mstore(add(data, 0x44), amount)
                                                          calldatacopy(add(data, 0x64), input.offset, input.length)
                                                          let status := call(gas(), asset, 0, data, add(0x64, input.length), 0x0, 0x20)
                                                          success := and(status, or(iszero(returndatasize()), and(gt(returndatasize(), 31), eq(mload(0), 1))))
                                                      }
                                                  }
                                              
                                                  function _getMakingAmount(
                                                      bytes calldata getter,
                                                      uint256 orderTakingAmount,
                                                      uint256 requestedTakingAmount,
                                                      uint256 orderMakingAmount,
                                                      uint256 remainingMakingAmount,
                                                      bytes32 orderHash
                                                  ) private view returns(uint256) {
                                                      if (getter.length == 0) {
                                                          // Linear proportion
                                                          return AmountCalculator.getMakingAmount(orderMakingAmount, orderTakingAmount, requestedTakingAmount);
                                                      }
                                                      return _callGetter(getter, orderTakingAmount, requestedTakingAmount, orderMakingAmount, remainingMakingAmount, orderHash);
                                                  }
                                              
                                                  function _getTakingAmount(
                                                      bytes calldata getter,
                                                      uint256 orderMakingAmount,
                                                      uint256 requestedMakingAmount,
                                                      uint256 orderTakingAmount,
                                                      uint256 remainingMakingAmount,
                                                      bytes32 orderHash
                                                  ) private view returns(uint256) {
                                                      if (getter.length == 0) {
                                                          // Linear proportion
                                                          return AmountCalculator.getTakingAmount(orderMakingAmount, orderTakingAmount, requestedMakingAmount);
                                                      }
                                                      return _callGetter(getter, orderMakingAmount, requestedMakingAmount, orderTakingAmount, remainingMakingAmount, orderHash);
                                                  }
                                              
                                                  function _callGetter(
                                                      bytes calldata getter,
                                                      uint256 orderExpectedAmount,
                                                      uint256 requestedAmount,
                                                      uint256 orderResultAmount,
                                                      uint256 remainingMakingAmount,
                                                      bytes32 orderHash
                                                  ) private view returns(uint256) {
                                                      if (getter.length == 1) {
                                                          if (OrderLib.getterIsFrozen(getter)) {
                                                              // On "x" getter calldata only exact amount is allowed
                                                              if (requestedAmount != orderExpectedAmount) revert WrongAmount();
                                                              return orderResultAmount;
                                                          } else {
                                                              revert WrongGetter();
                                                          }
                                                      } else {
                                                          (address target, bytes calldata data) = getter.decodeTargetAndCalldata();
                                                          (bool success, bytes memory result) = target.staticcall(abi.encodePacked(data, requestedAmount, remainingMakingAmount, orderHash));
                                                          if (!success || result.length != 32) revert GetAmountCallFailed();
                                                          return abi.decode(result, (uint256));
                                                      }
                                                  }
                                              }
                                              
                                              
                                              // File @openzeppelin/contracts/utils/[email protected]
                                              
                                              // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              /**
                                               * @dev Provides information about the current execution context, including the
                                               * sender of the transaction and its data. While these are generally available
                                               * via msg.sender and msg.data, they should not be accessed in such a direct
                                               * manner, since when dealing with meta-transactions the account sending and
                                               * paying for execution may not be the actual sender (as far as an application
                                               * is concerned).
                                               *
                                               * This contract is only required for intermediate, library-like contracts.
                                               */
                                              abstract contract Context {
                                                  function _msgSender() internal view virtual returns (address) {
                                                      return msg.sender;
                                                  }
                                              
                                                  function _msgData() internal view virtual returns (bytes calldata) {
                                                      return msg.data;
                                                  }
                                              }
                                              
                                              
                                              // File @openzeppelin/contracts/access/[email protected]
                                              
                                              // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
                                              
                                              pragma solidity ^0.8.0;
                                              
                                              /**
                                               * @dev Contract module which provides a basic access control mechanism, where
                                               * there is an account (an owner) that can be granted exclusive access to
                                               * specific functions.
                                               *
                                               * By default, the owner account will be the one that deploys the contract. This
                                               * can later be changed with {transferOwnership}.
                                               *
                                               * This module is used through inheritance. It will make available the modifier
                                               * `onlyOwner`, which can be applied to your functions to restrict their use to
                                               * the owner.
                                               */
                                              abstract contract Ownable is Context {
                                                  address private _owner;
                                              
                                                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                                              
                                                  /**
                                                   * @dev Initializes the contract setting the deployer as the initial owner.
                                                   */
                                                  constructor() {
                                                      _transferOwnership(_msgSender());
                                                  }
                                              
                                                  /**
                                                   * @dev Throws if called by any account other than the owner.
                                                   */
                                                  modifier onlyOwner() {
                                                      _checkOwner();
                                                      _;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the address of the current owner.
                                                   */
                                                  function owner() public view virtual returns (address) {
                                                      return _owner;
                                                  }
                                              
                                                  /**
                                                   * @dev Throws if the sender is not the owner.
                                                   */
                                                  function _checkOwner() internal view virtual {
                                                      require(owner() == _msgSender(), "Ownable: caller is not the owner");
                                                  }
                                              
                                                  /**
                                                   * @dev Leaves the contract without owner. It will not be possible to call
                                                   * `onlyOwner` functions anymore. Can only be called by the current owner.
                                                   *
                                                   * NOTE: Renouncing ownership will leave the contract without an owner,
                                                   * thereby removing any functionality that is only available to the owner.
                                                   */
                                                  function renounceOwnership() public virtual onlyOwner {
                                                      _transferOwnership(address(0));
                                                  }
                                              
                                                  /**
                                                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                                                   * Can only be called by the current owner.
                                                   */
                                                  function transferOwnership(address newOwner) public virtual onlyOwner {
                                                      require(newOwner != address(0), "Ownable: new owner is the zero address");
                                                      _transferOwnership(newOwner);
                                                  }
                                              
                                                  /**
                                                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                                                   * Internal function without access restriction.
                                                   */
                                                  function _transferOwnership(address newOwner) internal virtual {
                                                      address oldOwner = _owner;
                                                      _owner = newOwner;
                                                      emit OwnershipTransferred(oldOwner, newOwner);
                                                  }
                                              }
                                              
                                              
                                              // File contracts/AggregationRouterV5.sol
                                              
                                              
                                              pragma solidity 0.8.17;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              /// @notice Main contract incorporates a number of routers to perform swaps and limit orders protocol to fill limit orders
                                              contract AggregationRouterV5 is EIP712("1inch Aggregation Router", "5"), Ownable,
                                                  ClipperRouter, GenericRouter, UnoswapRouter, UnoswapV3Router, OrderMixin, OrderRFQMixin
                                              {
                                                  using UniERC20 for IERC20;
                                              
                                                  error ZeroAddress();
                                              
                                                  /**
                                                   * @dev Sets the wrapped eth token and clipper exhange interface
                                                   * Both values are immutable: they can only be set once during
                                                   * construction.
                                                   */
                                                  constructor(IWETH weth)
                                                      UnoswapV3Router(weth)
                                                      ClipperRouter(weth)
                                                      OrderMixin(weth)
                                                      OrderRFQMixin(weth)
                                                  {
                                                      if (address(weth) == address(0)) revert ZeroAddress();
                                                  }
                                              
                                                  /**
                                                   * @notice Retrieves funds accidently sent directly to the contract address
                                                   * @param token ERC20 token to retrieve
                                                   * @param amount amount to retrieve
                                                   */
                                                  function rescueFunds(IERC20 token, uint256 amount) external onlyOwner {
                                                      token.uniTransfer(payable(msg.sender), amount);
                                                  }
                                              
                                                  /**
                                                   * @notice Destroys the contract and sends eth to sender. Use with caution.
                                                   * The only case when the use of the method is justified is if there is an exploit found.
                                                   * And the damage from the exploit is greater than from just an urgent contract change.
                                                   */
                                                  function destroy() external onlyOwner {
                                                      selfdestruct(payable(msg.sender));
                                                  }
                                              
                                                  function _receive() internal override(EthReceiver, OnlyWethReceiver) {
                                                      EthReceiver._receive();
                                                  }
                                              }

                                              File 2 of 6: AppProxyUpgradeable
                                              // File: contracts/common/UnstructuredStorage.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              library UnstructuredStorage {
                                                  function getStorageBool(bytes32 position) internal view returns (bool data) {
                                                      assembly { data := sload(position) }
                                                  }
                                              
                                                  function getStorageAddress(bytes32 position) internal view returns (address data) {
                                                      assembly { data := sload(position) }
                                                  }
                                              
                                                  function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) {
                                                      assembly { data := sload(position) }
                                                  }
                                              
                                                  function getStorageUint256(bytes32 position) internal view returns (uint256 data) {
                                                      assembly { data := sload(position) }
                                                  }
                                              
                                                  function setStorageBool(bytes32 position, bool data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                              
                                                  function setStorageAddress(bytes32 position, address data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                              
                                                  function setStorageBytes32(bytes32 position, bytes32 data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                              
                                                  function setStorageUint256(bytes32 position, uint256 data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                              }
                                              
                                              // File: contracts/acl/IACL.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              interface IACL {
                                                  function initialize(address permissionsCreator) external;
                                              
                                                  // TODO: this should be external
                                                  // See https://github.com/ethereum/solidity/issues/4832
                                                  function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);
                                              }
                                              
                                              // File: contracts/common/IVaultRecoverable.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              interface IVaultRecoverable {
                                                  event RecoverToVault(address indexed vault, address indexed token, uint256 amount);
                                              
                                                  function transferToVault(address token) external;
                                              
                                                  function allowRecoverability(address token) external view returns (bool);
                                                  function getRecoveryVault() external view returns (address);
                                              }
                                              
                                              // File: contracts/kernel/IKernel.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              
                                              interface IKernelEvents {
                                                  event SetApp(bytes32 indexed namespace, bytes32 indexed appId, address app);
                                              }
                                              
                                              
                                              // This should be an interface, but interfaces can't inherit yet :(
                                              contract IKernel is IKernelEvents, IVaultRecoverable {
                                                  function acl() public view returns (IACL);
                                                  function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);
                                              
                                                  function setApp(bytes32 namespace, bytes32 appId, address app) public;
                                                  function getApp(bytes32 namespace, bytes32 appId) public view returns (address);
                                              }
                                              
                                              // File: contracts/apps/AppStorage.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              
                                              contract AppStorage {
                                                  using UnstructuredStorage for bytes32;
                                              
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant KERNEL_POSITION = keccak256("aragonOS.appStorage.kernel");
                                                  bytes32 internal constant APP_ID_POSITION = keccak256("aragonOS.appStorage.appId");
                                                  */
                                                  bytes32 internal constant KERNEL_POSITION = 0x4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b;
                                                  bytes32 internal constant APP_ID_POSITION = 0xd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b;
                                              
                                                  function kernel() public view returns (IKernel) {
                                                      return IKernel(KERNEL_POSITION.getStorageAddress());
                                                  }
                                              
                                                  function appId() public view returns (bytes32) {
                                                      return APP_ID_POSITION.getStorageBytes32();
                                                  }
                                              
                                                  function setKernel(IKernel _kernel) internal {
                                                      KERNEL_POSITION.setStorageAddress(address(_kernel));
                                                  }
                                              
                                                  function setAppId(bytes32 _appId) internal {
                                                      APP_ID_POSITION.setStorageBytes32(_appId);
                                                  }
                                              }
                                              
                                              // File: contracts/common/IsContract.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              contract IsContract {
                                                  /*
                                                  * NOTE: this should NEVER be used for authentication
                                                  * (see pitfalls: https://github.com/fergarrui/ethereum-security/tree/master/contracts/extcodesize).
                                                  *
                                                  * This is only intended to be used as a sanity check that an address is actually a contract,
                                                  * RATHER THAN an address not being a contract.
                                                  */
                                                  function isContract(address _target) internal view returns (bool) {
                                                      if (_target == address(0)) {
                                                          return false;
                                                      }
                                              
                                                      uint256 size;
                                                      assembly { size := extcodesize(_target) }
                                                      return size > 0;
                                                  }
                                              }
                                              
                                              // File: contracts/lib/misc/ERCProxy.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              contract ERCProxy {
                                                  uint256 internal constant FORWARDING = 1;
                                                  uint256 internal constant UPGRADEABLE = 2;
                                              
                                                  function proxyType() public pure returns (uint256 proxyTypeId);
                                                  function implementation() public view returns (address codeAddr);
                                              }
                                              
                                              // File: contracts/common/DelegateProxy.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              contract DelegateProxy is ERCProxy, IsContract {
                                                  uint256 internal constant FWD_GAS_LIMIT = 10000;
                                              
                                                  /**
                                                  * @dev Performs a delegatecall and returns whatever the delegatecall returned (entire context execution will return!)
                                                  * @param _dst Destination address to perform the delegatecall
                                                  * @param _calldata Calldata for the delegatecall
                                                  */
                                                  function delegatedFwd(address _dst, bytes _calldata) internal {
                                                      require(isContract(_dst));
                                                      uint256 fwdGasLimit = FWD_GAS_LIMIT;
                                              
                                                      assembly {
                                                          let result := delegatecall(sub(gas, fwdGasLimit), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0)
                                                          let size := returndatasize
                                                          let ptr := mload(0x40)
                                                          returndatacopy(ptr, 0, size)
                                              
                                                          // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
                                                          // if the call returned error data, forward it
                                                          switch result case 0 { revert(ptr, size) }
                                                          default { return(ptr, size) }
                                                      }
                                                  }
                                              }
                                              
                                              // File: contracts/common/DepositableStorage.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              contract DepositableStorage {
                                                  using UnstructuredStorage for bytes32;
                                              
                                                  // keccak256("aragonOS.depositableStorage.depositable")
                                                  bytes32 internal constant DEPOSITABLE_POSITION = 0x665fd576fbbe6f247aff98f5c94a561e3f71ec2d3c988d56f12d342396c50cea;
                                              
                                                  function isDepositable() public view returns (bool) {
                                                      return DEPOSITABLE_POSITION.getStorageBool();
                                                  }
                                              
                                                  function setDepositable(bool _depositable) internal {
                                                      DEPOSITABLE_POSITION.setStorageBool(_depositable);
                                                  }
                                              }
                                              
                                              // File: contracts/common/DepositableDelegateProxy.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              contract DepositableDelegateProxy is DepositableStorage, DelegateProxy {
                                                  event ProxyDeposit(address sender, uint256 value);
                                              
                                                  function () external payable {
                                                      uint256 forwardGasThreshold = FWD_GAS_LIMIT;
                                                      bytes32 isDepositablePosition = DEPOSITABLE_POSITION;
                                              
                                                      // Optimized assembly implementation to prevent EIP-1884 from breaking deposits, reference code in Solidity:
                                                      // https://github.com/aragon/aragonOS/blob/v4.2.1/contracts/common/DepositableDelegateProxy.sol#L10-L20
                                                      assembly {
                                                          // Continue only if the gas left is lower than the threshold for forwarding to the implementation code,
                                                          // otherwise continue outside of the assembly block.
                                                          if lt(gas, forwardGasThreshold) {
                                                              // Only accept the deposit and emit an event if all of the following are true:
                                                              // the proxy accepts deposits (isDepositable), msg.data.length == 0, and msg.value > 0
                                                              if and(and(sload(isDepositablePosition), iszero(calldatasize)), gt(callvalue, 0)) {
                                                                  // Equivalent Solidity code for emitting the event:
                                                                  // emit ProxyDeposit(msg.sender, msg.value);
                                              
                                                                  let logData := mload(0x40) // free memory pointer
                                                                  mstore(logData, caller) // add 'msg.sender' to the log data (first event param)
                                                                  mstore(add(logData, 0x20), callvalue) // add 'msg.value' to the log data (second event param)
                                              
                                                                  // Emit an event with one topic to identify the event: keccak256('ProxyDeposit(address,uint256)') = 0x15ee...dee1
                                                                  log1(logData, 0x40, 0x15eeaa57c7bd188c1388020bcadc2c436ec60d647d36ef5b9eb3c742217ddee1)
                                              
                                                                  stop() // Stop. Exits execution context
                                                              }
                                              
                                                              // If any of above checks failed, revert the execution (if ETH was sent, it is returned to the sender)
                                                              revert(0, 0)
                                                          }
                                                      }
                                              
                                                      address target = implementation();
                                                      delegatedFwd(target, msg.data);
                                                  }
                                              }
                                              
                                              // File: contracts/kernel/KernelConstants.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              contract KernelAppIds {
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant KERNEL_CORE_APP_ID = apmNamehash("kernel");
                                                  bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = apmNamehash("acl");
                                                  bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = apmNamehash("vault");
                                                  */
                                                  bytes32 internal constant KERNEL_CORE_APP_ID = 0x3b4bf6bf3ad5000ecf0f989d5befde585c6860fea3e574a4fab4c49d1c177d9c;
                                                  bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = 0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a;
                                                  bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = 0x7e852e0fcfce6551c13800f1e7476f982525c2b5277ba14b24339c68416336d1;
                                              }
                                              
                                              
                                              contract KernelNamespaceConstants {
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant KERNEL_CORE_NAMESPACE = keccak256("core");
                                                  bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = keccak256("base");
                                                  bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = keccak256("app");
                                                  */
                                                  bytes32 internal constant KERNEL_CORE_NAMESPACE = 0xc681a85306374a5ab27f0bbc385296a54bcd314a1948b6cf61c4ea1bc44bb9f8;
                                                  bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = 0xf1f3eb40f5bc1ad1344716ced8b8a0431d840b5783aea1fd01786bc26f35ac0f;
                                                  bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
                                              }
                                              
                                              // File: contracts/apps/AppProxyBase.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              contract AppProxyBase is AppStorage, DepositableDelegateProxy, KernelNamespaceConstants {
                                                  /**
                                                  * @dev Initialize AppProxy
                                                  * @param _kernel Reference to organization kernel for the app
                                                  * @param _appId Identifier for app
                                                  * @param _initializePayload Payload for call to be made after setup to initialize
                                                  */
                                                  constructor(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public {
                                                      setKernel(_kernel);
                                                      setAppId(_appId);
                                              
                                                      // Implicit check that kernel is actually a Kernel
                                                      // The EVM doesn't actually provide a way for us to make sure, but we can force a revert to
                                                      // occur if the kernel is set to 0x0 or a non-code address when we try to call a method on
                                                      // it.
                                                      address appCode = getAppBase(_appId);
                                              
                                                      // If initialize payload is provided, it will be executed
                                                      if (_initializePayload.length > 0) {
                                                          require(isContract(appCode));
                                                          // Cannot make delegatecall as a delegateproxy.delegatedFwd as it
                                                          // returns ending execution context and halts contract deployment
                                                          require(appCode.delegatecall(_initializePayload));
                                                      }
                                                  }
                                              
                                                  function getAppBase(bytes32 _appId) internal view returns (address) {
                                                      return kernel().getApp(KERNEL_APP_BASES_NAMESPACE, _appId);
                                                  }
                                              }
                                              
                                              // File: contracts/apps/AppProxyUpgradeable.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              contract AppProxyUpgradeable is AppProxyBase {
                                                  /**
                                                  * @dev Initialize AppProxyUpgradeable (makes it an upgradeable Aragon app)
                                                  * @param _kernel Reference to organization kernel for the app
                                                  * @param _appId Identifier for app
                                                  * @param _initializePayload Payload for call to be made after setup to initialize
                                                  */
                                                  constructor(IKernel _kernel, bytes32 _appId, bytes _initializePayload)
                                                      AppProxyBase(_kernel, _appId, _initializePayload)
                                                      public // solium-disable-line visibility-first
                                                  {
                                                      // solium-disable-previous-line no-empty-blocks
                                                  }
                                              
                                                  /**
                                                   * @dev ERC897, the address the proxy would delegate calls to
                                                   */
                                                  function implementation() public view returns (address) {
                                                      return getAppBase(appId());
                                                  }
                                              
                                                  /**
                                                   * @dev ERC897, whether it is a forwarding (1) or an upgradeable (2) proxy
                                                   */
                                                  function proxyType() public pure returns (uint256 proxyTypeId) {
                                                      return UPGRADEABLE;
                                                  }
                                              }

                                              File 3 of 6: WstETH
                                              // SPDX-License-Identifier: MIT AND GPL-3.0
                                              // File: @openzeppelin/contracts/utils/Context.sol
                                              
                                              
                                              pragma solidity >=0.6.0 <0.8.0;
                                              
                                              /*
                                               * @dev Provides information about the current execution context, including the
                                               * sender of the transaction and its data. While these are generally available
                                               * via msg.sender and msg.data, they should not be accessed in such a direct
                                               * manner, since when dealing with GSN meta-transactions the account sending and
                                               * paying for execution may not be the actual sender (as far as an application
                                               * is concerned).
                                               *
                                               * This contract is only required for intermediate, library-like contracts.
                                               */
                                              abstract contract Context {
                                                  function _msgSender() internal view virtual returns (address payable) {
                                                      return msg.sender;
                                                  }
                                              
                                                  function _msgData() internal view virtual returns (bytes memory) {
                                                      this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                                                      return msg.data;
                                                  }
                                              }
                                              
                                              // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
                                              
                                              
                                              pragma solidity >=0.6.0 <0.8.0;
                                              
                                              /**
                                               * @dev Interface of the ERC20 standard as defined in the EIP.
                                               */
                                              interface IERC20 {
                                                  /**
                                                   * @dev Returns the amount of tokens in existence.
                                                   */
                                                  function totalSupply() external view returns (uint256);
                                              
                                                  /**
                                                   * @dev Returns the amount of tokens owned by `account`.
                                                   */
                                                  function balanceOf(address account) external view returns (uint256);
                                              
                                                  /**
                                                   * @dev Moves `amount` tokens from the caller's account to `recipient`.
                                                   *
                                                   * Returns a boolean value indicating whether the operation succeeded.
                                                   *
                                                   * Emits a {Transfer} event.
                                                   */
                                                  function transfer(address recipient, uint256 amount) external returns (bool);
                                              
                                                  /**
                                                   * @dev Returns the remaining number of tokens that `spender` will be
                                                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                                                   * zero by default.
                                                   *
                                                   * This value changes when {approve} or {transferFrom} are called.
                                                   */
                                                  function allowance(address owner, address spender) external view returns (uint256);
                                              
                                                  /**
                                                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                                                   *
                                                   * Returns a boolean value indicating whether the operation succeeded.
                                                   *
                                                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                                                   * that someone may use both the old and the new allowance by unfortunate
                                                   * transaction ordering. One possible solution to mitigate this race
                                                   * condition is to first reduce the spender's allowance to 0 and set the
                                                   * desired value afterwards:
                                                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                                                   *
                                                   * Emits an {Approval} event.
                                                   */
                                                  function approve(address spender, uint256 amount) external returns (bool);
                                              
                                                  /**
                                                   * @dev Moves `amount` tokens from `sender` to `recipient` using the
                                                   * allowance mechanism. `amount` is then deducted from the caller's
                                                   * allowance.
                                                   *
                                                   * Returns a boolean value indicating whether the operation succeeded.
                                                   *
                                                   * Emits a {Transfer} event.
                                                   */
                                                  function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                                              
                                                  /**
                                                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                                                   * another (`to`).
                                                   *
                                                   * Note that `value` may be zero.
                                                   */
                                                  event Transfer(address indexed from, address indexed to, uint256 value);
                                              
                                                  /**
                                                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                                                   * a call to {approve}. `value` is the new allowance.
                                                   */
                                                  event Approval(address indexed owner, address indexed spender, uint256 value);
                                              }
                                              
                                              // File: @openzeppelin/contracts/math/SafeMath.sol
                                              
                                              
                                              pragma solidity >=0.6.0 <0.8.0;
                                              
                                              /**
                                               * @dev Wrappers over Solidity's arithmetic operations with added overflow
                                               * checks.
                                               *
                                               * Arithmetic operations in Solidity wrap on overflow. This can easily result
                                               * in bugs, because programmers usually assume that an overflow raises an
                                               * error, which is the standard behavior in high level programming languages.
                                               * `SafeMath` restores this intuition by reverting the transaction when an
                                               * operation overflows.
                                               *
                                               * Using this library instead of the unchecked operations eliminates an entire
                                               * class of bugs, so it's recommended to use it always.
                                               */
                                              library SafeMath {
                                                  /**
                                                   * @dev Returns the addition of two unsigned integers, with an overflow flag.
                                                   *
                                                   * _Available since v3.4._
                                                   */
                                                  function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                                      uint256 c = a + b;
                                                      if (c < a) return (false, 0);
                                                      return (true, c);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the substraction of two unsigned integers, with an overflow flag.
                                                   *
                                                   * _Available since v3.4._
                                                   */
                                                  function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                                      if (b > a) return (false, 0);
                                                      return (true, a - b);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
                                                   *
                                                   * _Available since v3.4._
                                                   */
                                                  function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                                      // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                                                      // benefit is lost if 'b' is also tested.
                                                      // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                                                      if (a == 0) return (true, 0);
                                                      uint256 c = a * b;
                                                      if (c / a != b) return (false, 0);
                                                      return (true, c);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the division of two unsigned integers, with a division by zero flag.
                                                   *
                                                   * _Available since v3.4._
                                                   */
                                                  function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                                      if (b == 0) return (false, 0);
                                                      return (true, a / b);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
                                                   *
                                                   * _Available since v3.4._
                                                   */
                                                  function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                                      if (b == 0) return (false, 0);
                                                      return (true, a % b);
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the addition of two unsigned integers, reverting on
                                                   * overflow.
                                                   *
                                                   * Counterpart to Solidity's `+` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - Addition cannot overflow.
                                                   */
                                                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                                                      uint256 c = a + b;
                                                      require(c >= a, "SafeMath: addition overflow");
                                                      return c;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the subtraction of two unsigned integers, reverting on
                                                   * overflow (when the result is negative).
                                                   *
                                                   * Counterpart to Solidity's `-` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - Subtraction cannot overflow.
                                                   */
                                                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                                                      require(b <= a, "SafeMath: subtraction overflow");
                                                      return a - b;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the multiplication of two unsigned integers, reverting on
                                                   * overflow.
                                                   *
                                                   * Counterpart to Solidity's `*` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - Multiplication cannot overflow.
                                                   */
                                                  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                                                      if (a == 0) return 0;
                                                      uint256 c = a * b;
                                                      require(c / a == b, "SafeMath: multiplication overflow");
                                                      return c;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the integer division of two unsigned integers, reverting on
                                                   * division by zero. The result is rounded towards zero.
                                                   *
                                                   * Counterpart to Solidity's `/` operator. Note: this function uses a
                                                   * `revert` opcode (which leaves remaining gas untouched) while Solidity
                                                   * uses an invalid opcode to revert (consuming all remaining gas).
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - The divisor cannot be zero.
                                                   */
                                                  function div(uint256 a, uint256 b) internal pure returns (uint256) {
                                                      require(b > 0, "SafeMath: division by zero");
                                                      return a / b;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                                                   * reverting when dividing by zero.
                                                   *
                                                   * Counterpart to Solidity's `%` operator. This function uses a `revert`
                                                   * opcode (which leaves remaining gas untouched) while Solidity uses an
                                                   * invalid opcode to revert (consuming all remaining gas).
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - The divisor cannot be zero.
                                                   */
                                                  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                                                      require(b > 0, "SafeMath: modulo by zero");
                                                      return a % b;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                                                   * overflow (when the result is negative).
                                                   *
                                                   * CAUTION: This function is deprecated because it requires allocating memory for the error
                                                   * message unnecessarily. For custom revert reasons use {trySub}.
                                                   *
                                                   * Counterpart to Solidity's `-` operator.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - Subtraction cannot overflow.
                                                   */
                                                  function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                                      require(b <= a, errorMessage);
                                                      return a - b;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the integer division of two unsigned integers, reverting with custom message on
                                                   * division by zero. The result is rounded towards zero.
                                                   *
                                                   * CAUTION: This function is deprecated because it requires allocating memory for the error
                                                   * message unnecessarily. For custom revert reasons use {tryDiv}.
                                                   *
                                                   * Counterpart to Solidity's `/` operator. Note: this function uses a
                                                   * `revert` opcode (which leaves remaining gas untouched) while Solidity
                                                   * uses an invalid opcode to revert (consuming all remaining gas).
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - The divisor cannot be zero.
                                                   */
                                                  function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                                      require(b > 0, errorMessage);
                                                      return a / b;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                                                   * reverting with custom message when dividing by zero.
                                                   *
                                                   * CAUTION: This function is deprecated because it requires allocating memory for the error
                                                   * message unnecessarily. For custom revert reasons use {tryMod}.
                                                   *
                                                   * Counterpart to Solidity's `%` operator. This function uses a `revert`
                                                   * opcode (which leaves remaining gas untouched) while Solidity uses an
                                                   * invalid opcode to revert (consuming all remaining gas).
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - The divisor cannot be zero.
                                                   */
                                                  function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                                      require(b > 0, errorMessage);
                                                      return a % b;
                                                  }
                                              }
                                              
                                              // File: @openzeppelin/contracts/token/ERC20/ERC20.sol
                                              
                                              
                                              pragma solidity >=0.6.0 <0.8.0;
                                              
                                              
                                              
                                              
                                              /**
                                               * @dev Implementation of the {IERC20} interface.
                                               *
                                               * This implementation is agnostic to the way tokens are created. This means
                                               * that a supply mechanism has to be added in a derived contract using {_mint}.
                                               * For a generic mechanism see {ERC20PresetMinterPauser}.
                                               *
                                               * TIP: For a detailed writeup see our guide
                                               * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
                                               * to implement supply mechanisms].
                                               *
                                               * We have followed general OpenZeppelin guidelines: functions revert instead
                                               * of returning `false` on failure. This behavior is nonetheless conventional
                                               * and does not conflict with the expectations of ERC20 applications.
                                               *
                                               * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
                                               * This allows applications to reconstruct the allowance for all accounts just
                                               * by listening to said events. Other implementations of the EIP may not emit
                                               * these events, as it isn't required by the specification.
                                               *
                                               * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
                                               * functions have been added to mitigate the well-known issues around setting
                                               * allowances. See {IERC20-approve}.
                                               */
                                              contract ERC20 is Context, IERC20 {
                                                  using SafeMath for uint256;
                                              
                                                  mapping (address => uint256) private _balances;
                                              
                                                  mapping (address => mapping (address => uint256)) private _allowances;
                                              
                                                  uint256 private _totalSupply;
                                              
                                                  string private _name;
                                                  string private _symbol;
                                                  uint8 private _decimals;
                                              
                                                  /**
                                                   * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
                                                   * a default value of 18.
                                                   *
                                                   * To select a different value for {decimals}, use {_setupDecimals}.
                                                   *
                                                   * All three of these values are immutable: they can only be set once during
                                                   * construction.
                                                   */
                                                  constructor (string memory name_, string memory symbol_) public {
                                                      _name = name_;
                                                      _symbol = symbol_;
                                                      _decimals = 18;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the name of the token.
                                                   */
                                                  function name() public view virtual returns (string memory) {
                                                      return _name;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the symbol of the token, usually a shorter version of the
                                                   * name.
                                                   */
                                                  function symbol() public view virtual returns (string memory) {
                                                      return _symbol;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the number of decimals used to get its user representation.
                                                   * For example, if `decimals` equals `2`, a balance of `505` tokens should
                                                   * be displayed to a user as `5,05` (`505 / 10 ** 2`).
                                                   *
                                                   * Tokens usually opt for a value of 18, imitating the relationship between
                                                   * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
                                                   * called.
                                                   *
                                                   * NOTE: This information is only used for _display_ purposes: it in
                                                   * no way affects any of the arithmetic of the contract, including
                                                   * {IERC20-balanceOf} and {IERC20-transfer}.
                                                   */
                                                  function decimals() public view virtual returns (uint8) {
                                                      return _decimals;
                                                  }
                                              
                                                  /**
                                                   * @dev See {IERC20-totalSupply}.
                                                   */
                                                  function totalSupply() public view virtual override returns (uint256) {
                                                      return _totalSupply;
                                                  }
                                              
                                                  /**
                                                   * @dev See {IERC20-balanceOf}.
                                                   */
                                                  function balanceOf(address account) public view virtual override returns (uint256) {
                                                      return _balances[account];
                                                  }
                                              
                                                  /**
                                                   * @dev See {IERC20-transfer}.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `recipient` cannot be the zero address.
                                                   * - the caller must have a balance of at least `amount`.
                                                   */
                                                  function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                                                      _transfer(_msgSender(), recipient, amount);
                                                      return true;
                                                  }
                                              
                                                  /**
                                                   * @dev See {IERC20-allowance}.
                                                   */
                                                  function allowance(address owner, address spender) public view virtual override returns (uint256) {
                                                      return _allowances[owner][spender];
                                                  }
                                              
                                                  /**
                                                   * @dev See {IERC20-approve}.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `spender` cannot be the zero address.
                                                   */
                                                  function approve(address spender, uint256 amount) public virtual override returns (bool) {
                                                      _approve(_msgSender(), spender, amount);
                                                      return true;
                                                  }
                                              
                                                  /**
                                                   * @dev See {IERC20-transferFrom}.
                                                   *
                                                   * Emits an {Approval} event indicating the updated allowance. This is not
                                                   * required by the EIP. See the note at the beginning of {ERC20}.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `sender` and `recipient` cannot be the zero address.
                                                   * - `sender` must have a balance of at least `amount`.
                                                   * - the caller must have allowance for ``sender``'s tokens of at least
                                                   * `amount`.
                                                   */
                                                  function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
                                                      _transfer(sender, recipient, amount);
                                                      _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                                                      return true;
                                                  }
                                              
                                                  /**
                                                   * @dev Atomically increases the allowance granted to `spender` by the caller.
                                                   *
                                                   * This is an alternative to {approve} that can be used as a mitigation for
                                                   * problems described in {IERC20-approve}.
                                                   *
                                                   * Emits an {Approval} event indicating the updated allowance.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `spender` cannot be the zero address.
                                                   */
                                                  function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                                                      _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                                                      return true;
                                                  }
                                              
                                                  /**
                                                   * @dev Atomically decreases the allowance granted to `spender` by the caller.
                                                   *
                                                   * This is an alternative to {approve} that can be used as a mitigation for
                                                   * problems described in {IERC20-approve}.
                                                   *
                                                   * Emits an {Approval} event indicating the updated allowance.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `spender` cannot be the zero address.
                                                   * - `spender` must have allowance for the caller of at least
                                                   * `subtractedValue`.
                                                   */
                                                  function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                                                      _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                                                      return true;
                                                  }
                                              
                                                  /**
                                                   * @dev Moves tokens `amount` from `sender` to `recipient`.
                                                   *
                                                   * This is internal function is equivalent to {transfer}, and can be used to
                                                   * e.g. implement automatic token fees, slashing mechanisms, etc.
                                                   *
                                                   * Emits a {Transfer} event.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `sender` cannot be the zero address.
                                                   * - `recipient` cannot be the zero address.
                                                   * - `sender` must have a balance of at least `amount`.
                                                   */
                                                  function _transfer(address sender, address recipient, uint256 amount) internal virtual {
                                                      require(sender != address(0), "ERC20: transfer from the zero address");
                                                      require(recipient != address(0), "ERC20: transfer to the zero address");
                                              
                                                      _beforeTokenTransfer(sender, recipient, amount);
                                              
                                                      _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                                                      _balances[recipient] = _balances[recipient].add(amount);
                                                      emit Transfer(sender, recipient, amount);
                                                  }
                                              
                                                  /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                                                   * the total supply.
                                                   *
                                                   * Emits a {Transfer} event with `from` set to the zero address.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `to` cannot be the zero address.
                                                   */
                                                  function _mint(address account, uint256 amount) internal virtual {
                                                      require(account != address(0), "ERC20: mint to the zero address");
                                              
                                                      _beforeTokenTransfer(address(0), account, amount);
                                              
                                                      _totalSupply = _totalSupply.add(amount);
                                                      _balances[account] = _balances[account].add(amount);
                                                      emit Transfer(address(0), account, amount);
                                                  }
                                              
                                                  /**
                                                   * @dev Destroys `amount` tokens from `account`, reducing the
                                                   * total supply.
                                                   *
                                                   * Emits a {Transfer} event with `to` set to the zero address.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `account` cannot be the zero address.
                                                   * - `account` must have at least `amount` tokens.
                                                   */
                                                  function _burn(address account, uint256 amount) internal virtual {
                                                      require(account != address(0), "ERC20: burn from the zero address");
                                              
                                                      _beforeTokenTransfer(account, address(0), amount);
                                              
                                                      _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                                                      _totalSupply = _totalSupply.sub(amount);
                                                      emit Transfer(account, address(0), amount);
                                                  }
                                              
                                                  /**
                                                   * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                                                   *
                                                   * This internal function is equivalent to `approve`, and can be used to
                                                   * e.g. set automatic allowances for certain subsystems, etc.
                                                   *
                                                   * Emits an {Approval} event.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `owner` cannot be the zero address.
                                                   * - `spender` cannot be the zero address.
                                                   */
                                                  function _approve(address owner, address spender, uint256 amount) internal virtual {
                                                      require(owner != address(0), "ERC20: approve from the zero address");
                                                      require(spender != address(0), "ERC20: approve to the zero address");
                                              
                                                      _allowances[owner][spender] = amount;
                                                      emit Approval(owner, spender, amount);
                                                  }
                                              
                                                  /**
                                                   * @dev Sets {decimals} to a value other than the default one of 18.
                                                   *
                                                   * WARNING: This function should only be called from the constructor. Most
                                                   * applications that interact with token contracts will not expect
                                                   * {decimals} to ever change, and may work incorrectly if it does.
                                                   */
                                                  function _setupDecimals(uint8 decimals_) internal virtual {
                                                      _decimals = decimals_;
                                                  }
                                              
                                                  /**
                                                   * @dev Hook that is called before any transfer of tokens. This includes
                                                   * minting and burning.
                                                   *
                                                   * Calling conditions:
                                                   *
                                                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                                                   * will be to transferred to `to`.
                                                   * - when `from` is zero, `amount` tokens will be minted for `to`.
                                                   * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                                                   * - `from` and `to` are never both zero.
                                                   *
                                                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                                                   */
                                                  function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
                                              }
                                              
                                              // File: @openzeppelin/contracts/drafts/IERC20Permit.sol
                                              
                                              
                                              pragma solidity >=0.6.0 <0.8.0;
                                              
                                              /**
                                               * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                                               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                                               *
                                               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                                               * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
                                               * need to send a transaction, and thus is not required to hold Ether at all.
                                               */
                                              interface IERC20Permit {
                                                  /**
                                                   * @dev Sets `value` as the allowance of `spender` over `owner`'s tokens,
                                                   * given `owner`'s signed approval.
                                                   *
                                                   * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                                                   * ordering also apply here.
                                                   *
                                                   * Emits an {Approval} event.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `spender` cannot be the zero address.
                                                   * - `deadline` must be a timestamp in the future.
                                                   * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                                                   * over the EIP712-formatted function arguments.
                                                   * - the signature must use ``owner``'s current nonce (see {nonces}).
                                                   *
                                                   * For more information on the signature format, see the
                                                   * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                                                   * section].
                                                   */
                                                  function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
                                              
                                                  /**
                                                   * @dev Returns the current nonce for `owner`. This value must be
                                                   * included whenever a signature is generated for {permit}.
                                                   *
                                                   * Every successful call to {permit} increases ``owner``'s nonce by one. This
                                                   * prevents a signature from being used multiple times.
                                                   */
                                                  function nonces(address owner) external view returns (uint256);
                                              
                                                  /**
                                                   * @dev Returns the domain separator used in the encoding of the signature for `permit`, as defined by {EIP712}.
                                                   */
                                                  // solhint-disable-next-line func-name-mixedcase
                                                  function DOMAIN_SEPARATOR() external view returns (bytes32);
                                              }
                                              
                                              // File: @openzeppelin/contracts/cryptography/ECDSA.sol
                                              
                                              
                                              pragma solidity >=0.6.0 <0.8.0;
                                              
                                              /**
                                               * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
                                               *
                                               * These functions can be used to verify that a message was signed by the holder
                                               * of the private keys of a given address.
                                               */
                                              library ECDSA {
                                                  /**
                                                   * @dev Returns the address that signed a hashed message (`hash`) with
                                                   * `signature`. This address can then be used for verification purposes.
                                                   *
                                                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                                                   * this function rejects them by requiring the `s` value to be in the lower
                                                   * half order, and the `v` value to be either 27 or 28.
                                                   *
                                                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                                                   * verification to be secure: it is possible to craft signatures that
                                                   * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                                                   * this is by receiving a hash of the original message (which may otherwise
                                                   * be too long), and then calling {toEthSignedMessageHash} on it.
                                                   */
                                                  function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                                                      // Check the signature length
                                                      if (signature.length != 65) {
                                                          revert("ECDSA: invalid signature length");
                                                      }
                                              
                                                      // Divide the signature in r, s and v variables
                                                      bytes32 r;
                                                      bytes32 s;
                                                      uint8 v;
                                              
                                                      // ecrecover takes the signature parameters, and the only way to get them
                                                      // currently is to use assembly.
                                                      // solhint-disable-next-line no-inline-assembly
                                                      assembly {
                                                          r := mload(add(signature, 0x20))
                                                          s := mload(add(signature, 0x40))
                                                          v := byte(0, mload(add(signature, 0x60)))
                                                      }
                                              
                                                      return recover(hash, v, r, s);
                                                  }
                                              
                                                  /**
                                                   * @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`,
                                                   * `r` and `s` signature fields separately.
                                                   */
                                                  function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
                                                      // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                                                      // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                                                      // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
                                                      // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                                                      //
                                                      // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                                                      // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                                                      // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                                                      // these malleable signatures as well.
                                                      require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
                                                      require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
                                              
                                                      // If the signature is valid (and not malleable), return the signer address
                                                      address signer = ecrecover(hash, v, r, s);
                                                      require(signer != address(0), "ECDSA: invalid signature");
                                              
                                                      return signer;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                                                   * replicates the behavior of the
                                                   * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
                                                   * JSON-RPC method.
                                                   *
                                                   * See {recover}.
                                                   */
                                                  function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
                                                      // 32 is the length in bytes of hash,
                                                      // enforced by the type signature above
                                                      return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
                                                  }
                                              }
                                              
                                              // File: @openzeppelin/contracts/utils/Counters.sol
                                              
                                              
                                              pragma solidity >=0.6.0 <0.8.0;
                                              
                                              
                                              /**
                                               * @title Counters
                                               * @author Matt Condon (@shrugs)
                                               * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
                                               * of elements in a mapping, issuing ERC721 ids, or counting request ids.
                                               *
                                               * Include with `using Counters for Counters.Counter;`
                                               * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath}
                                               * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
                                               * directly accessed.
                                               */
                                              library Counters {
                                                  using SafeMath for uint256;
                                              
                                                  struct Counter {
                                                      // This variable should never be directly accessed by users of the library: interactions must be restricted to
                                                      // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                                                      // this feature: see https://github.com/ethereum/solidity/issues/4637
                                                      uint256 _value; // default: 0
                                                  }
                                              
                                                  function current(Counter storage counter) internal view returns (uint256) {
                                                      return counter._value;
                                                  }
                                              
                                                  function increment(Counter storage counter) internal {
                                                      // The {SafeMath} overflow check can be skipped here, see the comment at the top
                                                      counter._value += 1;
                                                  }
                                              
                                                  function decrement(Counter storage counter) internal {
                                                      counter._value = counter._value.sub(1);
                                                  }
                                              }
                                              
                                              // File: @openzeppelin/contracts/drafts/EIP712.sol
                                              
                                              
                                              pragma solidity >=0.6.0 <0.8.0;
                                              
                                              /**
                                               * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
                                               *
                                               * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
                                               * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
                                               * they need in their contracts using a combination of `abi.encode` and `keccak256`.
                                               *
                                               * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
                                               * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
                                               * ({_hashTypedDataV4}).
                                               *
                                               * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
                                               * the chain id to protect against replay attacks on an eventual fork of the chain.
                                               *
                                               * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
                                               * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
                                               *
                                               * _Available since v3.4._
                                               */
                                              abstract contract EIP712 {
                                                  /* solhint-disable var-name-mixedcase */
                                                  // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
                                                  // invalidate the cached domain separator if the chain id changes.
                                                  bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
                                                  uint256 private immutable _CACHED_CHAIN_ID;
                                              
                                                  bytes32 private immutable _HASHED_NAME;
                                                  bytes32 private immutable _HASHED_VERSION;
                                                  bytes32 private immutable _TYPE_HASH;
                                                  /* solhint-enable var-name-mixedcase */
                                              
                                                  /**
                                                   * @dev Initializes the domain separator and parameter caches.
                                                   *
                                                   * The meaning of `name` and `version` is specified in
                                                   * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                                                   *
                                                   * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                                                   * - `version`: the current major version of the signing domain.
                                                   *
                                                   * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                                                   * contract upgrade].
                                                   */
                                                  constructor(string memory name, string memory version) internal {
                                                      bytes32 hashedName = keccak256(bytes(name));
                                                      bytes32 hashedVersion = keccak256(bytes(version));
                                                      bytes32 typeHash = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                                                      _HASHED_NAME = hashedName;
                                                      _HASHED_VERSION = hashedVersion;
                                                      _CACHED_CHAIN_ID = _getChainId();
                                                      _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
                                                      _TYPE_HASH = typeHash;
                                                  }
                                              
                                                  /**
                                                   * @dev Returns the domain separator for the current chain.
                                                   */
                                                  function _domainSeparatorV4() internal view virtual returns (bytes32) {
                                                      if (_getChainId() == _CACHED_CHAIN_ID) {
                                                          return _CACHED_DOMAIN_SEPARATOR;
                                                      } else {
                                                          return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
                                                      }
                                                  }
                                              
                                                  function _buildDomainSeparator(bytes32 typeHash, bytes32 name, bytes32 version) private view returns (bytes32) {
                                                      return keccak256(
                                                          abi.encode(
                                                              typeHash,
                                                              name,
                                                              version,
                                                              _getChainId(),
                                                              address(this)
                                                          )
                                                      );
                                                  }
                                              
                                                  /**
                                                   * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                                                   * function returns the hash of the fully encoded EIP712 message for this domain.
                                                   *
                                                   * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                                                   *
                                                   * ```solidity
                                                   * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                                                   *     keccak256("Mail(address to,string contents)"),
                                                   *     mailTo,
                                                   *     keccak256(bytes(mailContents))
                                                   * )));
                                                   * address signer = ECDSA.recover(digest, signature);
                                                   * ```
                                                   */
                                                  function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                                                      return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash));
                                                  }
                                              
                                                  function _getChainId() private view returns (uint256 chainId) {
                                                      this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                                                      // solhint-disable-next-line no-inline-assembly
                                                      assembly {
                                                          chainId := chainid()
                                                      }
                                                  }
                                              }
                                              
                                              // File: @openzeppelin/contracts/drafts/ERC20Permit.sol
                                              
                                              
                                              pragma solidity >=0.6.5 <0.8.0;
                                              
                                              
                                              
                                              
                                              
                                              
                                              /**
                                               * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                                               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                                               *
                                               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                                               * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
                                               * need to send a transaction, and thus is not required to hold Ether at all.
                                               *
                                               * _Available since v3.4._
                                               */
                                              abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
                                                  using Counters for Counters.Counter;
                                              
                                                  mapping (address => Counters.Counter) private _nonces;
                                              
                                                  // solhint-disable-next-line var-name-mixedcase
                                                  bytes32 private immutable _PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
                                              
                                                  /**
                                                   * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
                                                   *
                                                   * It's a good idea to use the same `name` that is defined as the ERC20 token name.
                                                   */
                                                  constructor(string memory name) internal EIP712(name, "1") {
                                                  }
                                              
                                                  /**
                                                   * @dev See {IERC20Permit-permit}.
                                                   */
                                                  function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual override {
                                                      // solhint-disable-next-line not-rely-on-time
                                                      require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
                                              
                                                      bytes32 structHash = keccak256(
                                                          abi.encode(
                                                              _PERMIT_TYPEHASH,
                                                              owner,
                                                              spender,
                                                              value,
                                                              _nonces[owner].current(),
                                                              deadline
                                                          )
                                                      );
                                              
                                                      bytes32 hash = _hashTypedDataV4(structHash);
                                              
                                                      address signer = ECDSA.recover(hash, v, r, s);
                                                      require(signer == owner, "ERC20Permit: invalid signature");
                                              
                                                      _nonces[owner].increment();
                                                      _approve(owner, spender, value);
                                                  }
                                              
                                                  /**
                                                   * @dev See {IERC20Permit-nonces}.
                                                   */
                                                  function nonces(address owner) public view override returns (uint256) {
                                                      return _nonces[owner].current();
                                                  }
                                              
                                                  /**
                                                   * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
                                                   */
                                                  // solhint-disable-next-line func-name-mixedcase
                                                  function DOMAIN_SEPARATOR() external view override returns (bytes32) {
                                                      return _domainSeparatorV4();
                                                  }
                                              }
                                              
                                              // File: contracts/0.6.12/interfaces/IStETH.sol
                                              
                                              // SPDX-FileCopyrightText: 2021 Lido <[email protected]>
                                              
                                              
                                              pragma solidity 0.6.12; // latest available for using OZ
                                              
                                              
                                              
                                              interface IStETH is IERC20 {
                                                  function getPooledEthByShares(uint256 _sharesAmount) external view returns (uint256);
                                              
                                                  function getSharesByPooledEth(uint256 _pooledEthAmount) external view returns (uint256);
                                              
                                                  function submit(address _referral) external payable returns (uint256);
                                              }
                                              
                                              // File: contracts/0.6.12/WstETH.sol
                                              
                                              // SPDX-FileCopyrightText: 2021 Lido <[email protected]>
                                              
                                              
                                              /* See contracts/COMPILERS.md */
                                              pragma solidity 0.6.12;
                                              
                                              
                                              
                                              /**
                                               * @title StETH token wrapper with static balances.
                                               * @dev It's an ERC20 token that represents the account's share of the total
                                               * supply of stETH tokens. WstETH token's balance only changes on transfers,
                                               * unlike StETH that is also changed when oracles report staking rewards and
                                               * penalties. It's a "power user" token for DeFi protocols which don't
                                               * support rebasable tokens.
                                               *
                                               * The contract is also a trustless wrapper that accepts stETH tokens and mints
                                               * wstETH in return. Then the user unwraps, the contract burns user's wstETH
                                               * and sends user locked stETH in return.
                                               *
                                               * The contract provides the staking shortcut: user can send ETH with regular
                                               * transfer and get wstETH in return. The contract will send ETH to Lido submit
                                               * method, staking it and wrapping the received stETH.
                                               *
                                               */
                                              contract WstETH is ERC20Permit {
                                                  IStETH public stETH;
                                              
                                                  /**
                                                   * @param _stETH address of the StETH token to wrap
                                                   */
                                                  constructor(IStETH _stETH)
                                                      public
                                                      ERC20Permit("Wrapped liquid staked Ether 2.0")
                                                      ERC20("Wrapped liquid staked Ether 2.0", "wstETH")
                                                  {
                                                      stETH = _stETH;
                                                  }
                                              
                                                  /**
                                                   * @notice Exchanges stETH to wstETH
                                                   * @param _stETHAmount amount of stETH to wrap in exchange for wstETH
                                                   * @dev Requirements:
                                                   *  - `_stETHAmount` must be non-zero
                                                   *  - msg.sender must approve at least `_stETHAmount` stETH to this
                                                   *    contract.
                                                   *  - msg.sender must have at least `_stETHAmount` of stETH.
                                                   * User should first approve _stETHAmount to the WstETH contract
                                                   * @return Amount of wstETH user receives after wrap
                                                   */
                                                  function wrap(uint256 _stETHAmount) external returns (uint256) {
                                                      require(_stETHAmount > 0, "wstETH: can't wrap zero stETH");
                                                      uint256 wstETHAmount = stETH.getSharesByPooledEth(_stETHAmount);
                                                      _mint(msg.sender, wstETHAmount);
                                                      stETH.transferFrom(msg.sender, address(this), _stETHAmount);
                                                      return wstETHAmount;
                                                  }
                                              
                                                  /**
                                                   * @notice Exchanges wstETH to stETH
                                                   * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH
                                                   * @dev Requirements:
                                                   *  - `_wstETHAmount` must be non-zero
                                                   *  - msg.sender must have at least `_wstETHAmount` wstETH.
                                                   * @return Amount of stETH user receives after unwrap
                                                   */
                                                  function unwrap(uint256 _wstETHAmount) external returns (uint256) {
                                                      require(_wstETHAmount > 0, "wstETH: zero amount unwrap not allowed");
                                                      uint256 stETHAmount = stETH.getPooledEthByShares(_wstETHAmount);
                                                      _burn(msg.sender, _wstETHAmount);
                                                      stETH.transfer(msg.sender, stETHAmount);
                                                      return stETHAmount;
                                                  }
                                              
                                                  /**
                                                  * @notice Shortcut to stake ETH and auto-wrap returned stETH
                                                  */
                                                  receive() external payable {
                                                      uint256 shares = stETH.submit{value: msg.value}(address(0));
                                                      _mint(msg.sender, shares);
                                                  }
                                              
                                                  /**
                                                   * @notice Get amount of wstETH for a given amount of stETH
                                                   * @param _stETHAmount amount of stETH
                                                   * @return Amount of wstETH for a given stETH amount
                                                   */
                                                  function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256) {
                                                      return stETH.getSharesByPooledEth(_stETHAmount);
                                                  }
                                              
                                                  /**
                                                   * @notice Get amount of stETH for a given amount of wstETH
                                                   * @param _wstETHAmount amount of wstETH
                                                   * @return Amount of stETH for a given wstETH amount
                                                   */
                                                  function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256) {
                                                      return stETH.getPooledEthByShares(_wstETHAmount);
                                                  }
                                              
                                                  /**
                                                   * @notice Get amount of stETH for a one wstETH
                                                   * @return Amount of stETH for 1 wstETH
                                                   */
                                                  function stEthPerToken() external view returns (uint256) {
                                                      return stETH.getPooledEthByShares(1 ether);
                                                  }
                                              
                                                  /**
                                                   * @notice Get amount of wstETH for a one stETH
                                                   * @return Amount of wstETH for a 1 stETH
                                                   */
                                                  function tokensPerStEth() external view returns (uint256) {
                                                      return stETH.getSharesByPooledEth(1 ether);
                                                  }
                                              }

                                              File 4 of 6: KernelProxy
                                              /**
                                               *Submitted for verification at Etherscan.io on 2020-02-06
                                              */
                                              
                                              // File: contracts/acl/IACL.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              interface IACL {
                                                  function initialize(address permissionsCreator) external;
                                              
                                                  // TODO: this should be external
                                                  // See https://github.com/ethereum/solidity/issues/4832
                                                  function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);
                                              }
                                              
                                              // File: contracts/common/IVaultRecoverable.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              interface IVaultRecoverable {
                                                  event RecoverToVault(address indexed vault, address indexed token, uint256 amount);
                                              
                                                  function transferToVault(address token) external;
                                              
                                                  function allowRecoverability(address token) external view returns (bool);
                                                  function getRecoveryVault() external view returns (address);
                                              }
                                              
                                              // File: contracts/kernel/IKernel.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              
                                              interface IKernelEvents {
                                                  event SetApp(bytes32 indexed namespace, bytes32 indexed appId, address app);
                                              }
                                              
                                              
                                              // This should be an interface, but interfaces can't inherit yet :(
                                              contract IKernel is IKernelEvents, IVaultRecoverable {
                                                  function acl() public view returns (IACL);
                                                  function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);
                                              
                                                  function setApp(bytes32 namespace, bytes32 appId, address app) public;
                                                  function getApp(bytes32 namespace, bytes32 appId) public view returns (address);
                                              }
                                              
                                              // File: contracts/kernel/KernelConstants.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              contract KernelAppIds {
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant KERNEL_CORE_APP_ID = apmNamehash("kernel");
                                                  bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = apmNamehash("acl");
                                                  bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = apmNamehash("vault");
                                                  */
                                                  bytes32 internal constant KERNEL_CORE_APP_ID = 0x3b4bf6bf3ad5000ecf0f989d5befde585c6860fea3e574a4fab4c49d1c177d9c;
                                                  bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = 0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a;
                                                  bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = 0x7e852e0fcfce6551c13800f1e7476f982525c2b5277ba14b24339c68416336d1;
                                              }
                                              
                                              
                                              contract KernelNamespaceConstants {
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant KERNEL_CORE_NAMESPACE = keccak256("core");
                                                  bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = keccak256("base");
                                                  bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = keccak256("app");
                                                  */
                                                  bytes32 internal constant KERNEL_CORE_NAMESPACE = 0xc681a85306374a5ab27f0bbc385296a54bcd314a1948b6cf61c4ea1bc44bb9f8;
                                                  bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = 0xf1f3eb40f5bc1ad1344716ced8b8a0431d840b5783aea1fd01786bc26f35ac0f;
                                                  bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
                                              }
                                              
                                              // File: contracts/kernel/KernelStorage.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              contract KernelStorage {
                                                  // namespace => app id => address
                                                  mapping (bytes32 => mapping (bytes32 => address)) public apps;
                                                  bytes32 public recoveryVaultAppId;
                                              }
                                              
                                              // File: contracts/acl/ACLSyntaxSugar.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              contract ACLSyntaxSugar {
                                                  function arr() internal pure returns (uint256[]) {
                                                      return new uint256[](0);
                                                  }
                                              
                                                  function arr(bytes32 _a) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a));
                                                  }
                                              
                                                  function arr(bytes32 _a, bytes32 _b) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b));
                                                  }
                                              
                                                  function arr(address _a) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a));
                                                  }
                                              
                                                  function arr(address _a, address _b) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b));
                                                  }
                                              
                                                  function arr(address _a, uint256 _b, uint256 _c) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), _b, _c);
                                                  }
                                              
                                                  function arr(address _a, uint256 _b, uint256 _c, uint256 _d) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), _b, _c, _d);
                                                  }
                                              
                                                  function arr(address _a, uint256 _b) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b));
                                                  }
                                              
                                                  function arr(address _a, address _b, uint256 _c, uint256 _d, uint256 _e) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b), _c, _d, _e);
                                                  }
                                              
                                                  function arr(address _a, address _b, address _c) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b), uint256(_c));
                                                  }
                                              
                                                  function arr(address _a, address _b, uint256 _c) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b), uint256(_c));
                                                  }
                                              
                                                  function arr(uint256 _a) internal pure returns (uint256[] r) {
                                                      r = new uint256[](1);
                                                      r[0] = _a;
                                                  }
                                              
                                                  function arr(uint256 _a, uint256 _b) internal pure returns (uint256[] r) {
                                                      r = new uint256[](2);
                                                      r[0] = _a;
                                                      r[1] = _b;
                                                  }
                                              
                                                  function arr(uint256 _a, uint256 _b, uint256 _c) internal pure returns (uint256[] r) {
                                                      r = new uint256[](3);
                                                      r[0] = _a;
                                                      r[1] = _b;
                                                      r[2] = _c;
                                                  }
                                              
                                                  function arr(uint256 _a, uint256 _b, uint256 _c, uint256 _d) internal pure returns (uint256[] r) {
                                                      r = new uint256[](4);
                                                      r[0] = _a;
                                                      r[1] = _b;
                                                      r[2] = _c;
                                                      r[3] = _d;
                                                  }
                                              
                                                  function arr(uint256 _a, uint256 _b, uint256 _c, uint256 _d, uint256 _e) internal pure returns (uint256[] r) {
                                                      r = new uint256[](5);
                                                      r[0] = _a;
                                                      r[1] = _b;
                                                      r[2] = _c;
                                                      r[3] = _d;
                                                      r[4] = _e;
                                                  }
                                              }
                                              
                                              
                                              contract ACLHelpers {
                                                  function decodeParamOp(uint256 _x) internal pure returns (uint8 b) {
                                                      return uint8(_x >> (8 * 30));
                                                  }
                                              
                                                  function decodeParamId(uint256 _x) internal pure returns (uint8 b) {
                                                      return uint8(_x >> (8 * 31));
                                                  }
                                              
                                                  function decodeParamsList(uint256 _x) internal pure returns (uint32 a, uint32 b, uint32 c) {
                                                      a = uint32(_x);
                                                      b = uint32(_x >> (8 * 4));
                                                      c = uint32(_x >> (8 * 8));
                                                  }
                                              }
                                              
                                              // File: contracts/common/ConversionHelpers.sol
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              library ConversionHelpers {
                                                  string private constant ERROR_IMPROPER_LENGTH = "CONVERSION_IMPROPER_LENGTH";
                                              
                                                  function dangerouslyCastUintArrayToBytes(uint256[] memory _input) internal pure returns (bytes memory output) {
                                                      // Force cast the uint256[] into a bytes array, by overwriting its length
                                                      // Note that the bytes array doesn't need to be initialized as we immediately overwrite it
                                                      // with the input and a new length. The input becomes invalid from this point forward.
                                                      uint256 byteLength = _input.length * 32;
                                                      assembly {
                                                          output := _input
                                                          mstore(output, byteLength)
                                                      }
                                                  }
                                              
                                                  function dangerouslyCastBytesToUintArray(bytes memory _input) internal pure returns (uint256[] memory output) {
                                                      // Force cast the bytes array into a uint256[], by overwriting its length
                                                      // Note that the uint256[] doesn't need to be initialized as we immediately overwrite it
                                                      // with the input and a new length. The input becomes invalid from this point forward.
                                                      uint256 intsLength = _input.length / 32;
                                                      require(_input.length == intsLength * 32, ERROR_IMPROPER_LENGTH);
                                              
                                                      assembly {
                                                          output := _input
                                                          mstore(output, intsLength)
                                                      }
                                                  }
                                              }
                                              
                                              // File: contracts/common/IsContract.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              contract IsContract {
                                                  /*
                                                  * NOTE: this should NEVER be used for authentication
                                                  * (see pitfalls: https://github.com/fergarrui/ethereum-security/tree/master/contracts/extcodesize).
                                                  *
                                                  * This is only intended to be used as a sanity check that an address is actually a contract,
                                                  * RATHER THAN an address not being a contract.
                                                  */
                                                  function isContract(address _target) internal view returns (bool) {
                                                      if (_target == address(0)) {
                                                          return false;
                                                      }
                                              
                                                      uint256 size;
                                                      assembly { size := extcodesize(_target) }
                                                      return size > 0;
                                                  }
                                              }
                                              
                                              // File: contracts/common/Uint256Helpers.sol
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              library Uint256Helpers {
                                                  uint256 private constant MAX_UINT64 = uint64(-1);
                                              
                                                  string private constant ERROR_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG";
                                              
                                                  function toUint64(uint256 a) internal pure returns (uint64) {
                                                      require(a <= MAX_UINT64, ERROR_NUMBER_TOO_BIG);
                                                      return uint64(a);
                                                  }
                                              }
                                              
                                              // File: contracts/common/TimeHelpers.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              contract TimeHelpers {
                                                  using Uint256Helpers for uint256;
                                              
                                                  /**
                                                  * @dev Returns the current block number.
                                                  *      Using a function rather than `block.number` allows us to easily mock the block number in
                                                  *      tests.
                                                  */
                                                  function getBlockNumber() internal view returns (uint256) {
                                                      return block.number;
                                                  }
                                              
                                                  /**
                                                  * @dev Returns the current block number, converted to uint64.
                                                  *      Using a function rather than `block.number` allows us to easily mock the block number in
                                                  *      tests.
                                                  */
                                                  function getBlockNumber64() internal view returns (uint64) {
                                                      return getBlockNumber().toUint64();
                                                  }
                                              
                                                  /**
                                                  * @dev Returns the current timestamp.
                                                  *      Using a function rather than `block.timestamp` allows us to easily mock it in
                                                  *      tests.
                                                  */
                                                  function getTimestamp() internal view returns (uint256) {
                                                      return block.timestamp; // solium-disable-line security/no-block-members
                                                  }
                                              
                                                  /**
                                                  * @dev Returns the current timestamp, converted to uint64.
                                                  *      Using a function rather than `block.timestamp` allows us to easily mock it in
                                                  *      tests.
                                                  */
                                                  function getTimestamp64() internal view returns (uint64) {
                                                      return getTimestamp().toUint64();
                                                  }
                                              }
                                              
                                              // File: contracts/common/UnstructuredStorage.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              library UnstructuredStorage {
                                                  function getStorageBool(bytes32 position) internal view returns (bool data) {
                                                      assembly { data := sload(position) }
                                                  }
                                              
                                                  function getStorageAddress(bytes32 position) internal view returns (address data) {
                                                      assembly { data := sload(position) }
                                                  }
                                              
                                                  function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) {
                                                      assembly { data := sload(position) }
                                                  }
                                              
                                                  function getStorageUint256(bytes32 position) internal view returns (uint256 data) {
                                                      assembly { data := sload(position) }
                                                  }
                                              
                                                  function setStorageBool(bytes32 position, bool data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                              
                                                  function setStorageAddress(bytes32 position, address data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                              
                                                  function setStorageBytes32(bytes32 position, bytes32 data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                              
                                                  function setStorageUint256(bytes32 position, uint256 data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                              }
                                              
                                              // File: contracts/common/Initializable.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              
                                              contract Initializable is TimeHelpers {
                                                  using UnstructuredStorage for bytes32;
                                              
                                                  // keccak256("aragonOS.initializable.initializationBlock")
                                                  bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e;
                                              
                                                  string private constant ERROR_ALREADY_INITIALIZED = "INIT_ALREADY_INITIALIZED";
                                                  string private constant ERROR_NOT_INITIALIZED = "INIT_NOT_INITIALIZED";
                                              
                                                  modifier onlyInit {
                                                      require(getInitializationBlock() == 0, ERROR_ALREADY_INITIALIZED);
                                                      _;
                                                  }
                                              
                                                  modifier isInitialized {
                                                      require(hasInitialized(), ERROR_NOT_INITIALIZED);
                                                      _;
                                                  }
                                              
                                                  /**
                                                  * @return Block number in which the contract was initialized
                                                  */
                                                  function getInitializationBlock() public view returns (uint256) {
                                                      return INITIALIZATION_BLOCK_POSITION.getStorageUint256();
                                                  }
                                              
                                                  /**
                                                  * @return Whether the contract has been initialized by the time of the current block
                                                  */
                                                  function hasInitialized() public view returns (bool) {
                                                      uint256 initializationBlock = getInitializationBlock();
                                                      return initializationBlock != 0 && getBlockNumber() >= initializationBlock;
                                                  }
                                              
                                                  /**
                                                  * @dev Function to be called by top level contract after initialization has finished.
                                                  */
                                                  function initialized() internal onlyInit {
                                                      INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber());
                                                  }
                                              
                                                  /**
                                                  * @dev Function to be called by top level contract after initialization to enable the contract
                                                  *      at a future block number rather than immediately.
                                                  */
                                                  function initializedAt(uint256 _blockNumber) internal onlyInit {
                                                      INITIALIZATION_BLOCK_POSITION.setStorageUint256(_blockNumber);
                                                  }
                                              }
                                              
                                              // File: contracts/common/Petrifiable.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              contract Petrifiable is Initializable {
                                                  // Use block UINT256_MAX (which should be never) as the initializable date
                                                  uint256 internal constant PETRIFIED_BLOCK = uint256(-1);
                                              
                                                  function isPetrified() public view returns (bool) {
                                                      return getInitializationBlock() == PETRIFIED_BLOCK;
                                                  }
                                              
                                                  /**
                                                  * @dev Function to be called by top level contract to prevent being initialized.
                                                  *      Useful for freezing base contracts when they're used behind proxies.
                                                  */
                                                  function petrify() internal onlyInit {
                                                      initializedAt(PETRIFIED_BLOCK);
                                                  }
                                              }
                                              
                                              // File: contracts/lib/token/ERC20.sol
                                              
                                              // See https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a9f910d34f0ab33a1ae5e714f69f9596a02b4d91/contracts/token/ERC20/ERC20.sol
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              /**
                                               * @title ERC20 interface
                                               * @dev see https://github.com/ethereum/EIPs/issues/20
                                               */
                                              contract ERC20 {
                                                  function totalSupply() public view returns (uint256);
                                              
                                                  function balanceOf(address _who) public view returns (uint256);
                                              
                                                  function allowance(address _owner, address _spender)
                                                      public view returns (uint256);
                                              
                                                  function transfer(address _to, uint256 _value) public returns (bool);
                                              
                                                  function approve(address _spender, uint256 _value)
                                                      public returns (bool);
                                              
                                                  function transferFrom(address _from, address _to, uint256 _value)
                                                      public returns (bool);
                                              
                                                  event Transfer(
                                                      address indexed from,
                                                      address indexed to,
                                                      uint256 value
                                                  );
                                              
                                                  event Approval(
                                                      address indexed owner,
                                                      address indexed spender,
                                                      uint256 value
                                                  );
                                              }
                                              
                                              // File: contracts/common/EtherTokenConstant.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              // aragonOS and aragon-apps rely on address(0) to denote native ETH, in
                                              // contracts where both tokens and ETH are accepted
                                              contract EtherTokenConstant {
                                                  address internal constant ETH = address(0);
                                              }
                                              
                                              // File: contracts/common/SafeERC20.sol
                                              
                                              // Inspired by AdEx (https://github.com/AdExNetwork/adex-protocol-eth/blob/b9df617829661a7518ee10f4cb6c4108659dd6d5/contracts/libs/SafeERC20.sol)
                                              // and 0x (https://github.com/0xProject/0x-monorepo/blob/737d1dc54d72872e24abce5a1dbe1b66d35fa21a/contracts/protocol/contracts/protocol/AssetProxy/ERC20Proxy.sol#L143)
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              library SafeERC20 {
                                                  // Before 0.5, solidity has a mismatch between `address.transfer()` and `token.transfer()`:
                                                  // https://github.com/ethereum/solidity/issues/3544
                                                  bytes4 private constant TRANSFER_SELECTOR = 0xa9059cbb;
                                              
                                                  string private constant ERROR_TOKEN_BALANCE_REVERTED = "SAFE_ERC_20_BALANCE_REVERTED";
                                                  string private constant ERROR_TOKEN_ALLOWANCE_REVERTED = "SAFE_ERC_20_ALLOWANCE_REVERTED";
                                              
                                                  function invokeAndCheckSuccess(address _addr, bytes memory _calldata)
                                                      private
                                                      returns (bool)
                                                  {
                                                      bool ret;
                                                      assembly {
                                                          let ptr := mload(0x40)    // free memory pointer
                                              
                                                          let success := call(
                                                              gas,                  // forward all gas
                                                              _addr,                // address
                                                              0,                    // no value
                                                              add(_calldata, 0x20), // calldata start
                                                              mload(_calldata),     // calldata length
                                                              ptr,                  // write output over free memory
                                                              0x20                  // uint256 return
                                                          )
                                              
                                                          if gt(success, 0) {
                                                              // Check number of bytes returned from last function call
                                                              switch returndatasize
                                              
                                                              // No bytes returned: assume success
                                                              case 0 {
                                                                  ret := 1
                                                              }
                                              
                                                              // 32 bytes returned: check if non-zero
                                                              case 0x20 {
                                                                  // Only return success if returned data was true
                                                                  // Already have output in ptr
                                                                  ret := eq(mload(ptr), 1)
                                                              }
                                              
                                                              // Not sure what was returned: don't mark as success
                                                              default { }
                                                          }
                                                      }
                                                      return ret;
                                                  }
                                              
                                                  function staticInvoke(address _addr, bytes memory _calldata)
                                                      private
                                                      view
                                                      returns (bool, uint256)
                                                  {
                                                      bool success;
                                                      uint256 ret;
                                                      assembly {
                                                          let ptr := mload(0x40)    // free memory pointer
                                              
                                                          success := staticcall(
                                                              gas,                  // forward all gas
                                                              _addr,                // address
                                                              add(_calldata, 0x20), // calldata start
                                                              mload(_calldata),     // calldata length
                                                              ptr,                  // write output over free memory
                                                              0x20                  // uint256 return
                                                          )
                                              
                                                          if gt(success, 0) {
                                                              ret := mload(ptr)
                                                          }
                                                      }
                                                      return (success, ret);
                                                  }
                                              
                                                  /**
                                                  * @dev Same as a standards-compliant ERC20.transfer() that never reverts (returns false).
                                                  *      Note that this makes an external call to the token.
                                                  */
                                                  function safeTransfer(ERC20 _token, address _to, uint256 _amount) internal returns (bool) {
                                                      bytes memory transferCallData = abi.encodeWithSelector(
                                                          TRANSFER_SELECTOR,
                                                          _to,
                                                          _amount
                                                      );
                                                      return invokeAndCheckSuccess(_token, transferCallData);
                                                  }
                                              
                                                  /**
                                                  * @dev Same as a standards-compliant ERC20.transferFrom() that never reverts (returns false).
                                                  *      Note that this makes an external call to the token.
                                                  */
                                                  function safeTransferFrom(ERC20 _token, address _from, address _to, uint256 _amount) internal returns (bool) {
                                                      bytes memory transferFromCallData = abi.encodeWithSelector(
                                                          _token.transferFrom.selector,
                                                          _from,
                                                          _to,
                                                          _amount
                                                      );
                                                      return invokeAndCheckSuccess(_token, transferFromCallData);
                                                  }
                                              
                                                  /**
                                                  * @dev Same as a standards-compliant ERC20.approve() that never reverts (returns false).
                                                  *      Note that this makes an external call to the token.
                                                  */
                                                  function safeApprove(ERC20 _token, address _spender, uint256 _amount) internal returns (bool) {
                                                      bytes memory approveCallData = abi.encodeWithSelector(
                                                          _token.approve.selector,
                                                          _spender,
                                                          _amount
                                                      );
                                                      return invokeAndCheckSuccess(_token, approveCallData);
                                                  }
                                              
                                                  /**
                                                  * @dev Static call into ERC20.balanceOf().
                                                  * Reverts if the call fails for some reason (should never fail).
                                                  */
                                                  function staticBalanceOf(ERC20 _token, address _owner) internal view returns (uint256) {
                                                      bytes memory balanceOfCallData = abi.encodeWithSelector(
                                                          _token.balanceOf.selector,
                                                          _owner
                                                      );
                                              
                                                      (bool success, uint256 tokenBalance) = staticInvoke(_token, balanceOfCallData);
                                                      require(success, ERROR_TOKEN_BALANCE_REVERTED);
                                              
                                                      return tokenBalance;
                                                  }
                                              
                                                  /**
                                                  * @dev Static call into ERC20.allowance().
                                                  * Reverts if the call fails for some reason (should never fail).
                                                  */
                                                  function staticAllowance(ERC20 _token, address _owner, address _spender) internal view returns (uint256) {
                                                      bytes memory allowanceCallData = abi.encodeWithSelector(
                                                          _token.allowance.selector,
                                                          _owner,
                                                          _spender
                                                      );
                                              
                                                      (bool success, uint256 allowance) = staticInvoke(_token, allowanceCallData);
                                                      require(success, ERROR_TOKEN_ALLOWANCE_REVERTED);
                                              
                                                      return allowance;
                                                  }
                                              
                                                  /**
                                                  * @dev Static call into ERC20.totalSupply().
                                                  * Reverts if the call fails for some reason (should never fail).
                                                  */
                                                  function staticTotalSupply(ERC20 _token) internal view returns (uint256) {
                                                      bytes memory totalSupplyCallData = abi.encodeWithSelector(_token.totalSupply.selector);
                                              
                                                      (bool success, uint256 totalSupply) = staticInvoke(_token, totalSupplyCallData);
                                                      require(success, ERROR_TOKEN_ALLOWANCE_REVERTED);
                                              
                                                      return totalSupply;
                                                  }
                                              }
                                              
                                              // File: contracts/common/VaultRecoverable.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              contract VaultRecoverable is IVaultRecoverable, EtherTokenConstant, IsContract {
                                                  using SafeERC20 for ERC20;
                                              
                                                  string private constant ERROR_DISALLOWED = "RECOVER_DISALLOWED";
                                                  string private constant ERROR_VAULT_NOT_CONTRACT = "RECOVER_VAULT_NOT_CONTRACT";
                                                  string private constant ERROR_TOKEN_TRANSFER_FAILED = "RECOVER_TOKEN_TRANSFER_FAILED";
                                              
                                                  /**
                                                   * @notice Send funds to recovery Vault. This contract should never receive funds,
                                                   *         but in case it does, this function allows one to recover them.
                                                   * @param _token Token balance to be sent to recovery vault.
                                                   */
                                                  function transferToVault(address _token) external {
                                                      require(allowRecoverability(_token), ERROR_DISALLOWED);
                                                      address vault = getRecoveryVault();
                                                      require(isContract(vault), ERROR_VAULT_NOT_CONTRACT);
                                              
                                                      uint256 balance;
                                                      if (_token == ETH) {
                                                          balance = address(this).balance;
                                                          vault.transfer(balance);
                                                      } else {
                                                          ERC20 token = ERC20(_token);
                                                          balance = token.staticBalanceOf(this);
                                                          require(token.safeTransfer(vault, balance), ERROR_TOKEN_TRANSFER_FAILED);
                                                      }
                                              
                                                      emit RecoverToVault(vault, _token, balance);
                                                  }
                                              
                                                  /**
                                                  * @dev By default deriving from AragonApp makes it recoverable
                                                  * @param token Token address that would be recovered
                                                  * @return bool whether the app allows the recovery
                                                  */
                                                  function allowRecoverability(address token) public view returns (bool) {
                                                      return true;
                                                  }
                                              
                                                  // Cast non-implemented interface to be public so we can use it internally
                                                  function getRecoveryVault() public view returns (address);
                                              }
                                              
                                              // File: contracts/apps/AppStorage.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              
                                              contract AppStorage {
                                                  using UnstructuredStorage for bytes32;
                                              
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant KERNEL_POSITION = keccak256("aragonOS.appStorage.kernel");
                                                  bytes32 internal constant APP_ID_POSITION = keccak256("aragonOS.appStorage.appId");
                                                  */
                                                  bytes32 internal constant KERNEL_POSITION = 0x4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b;
                                                  bytes32 internal constant APP_ID_POSITION = 0xd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b;
                                              
                                                  function kernel() public view returns (IKernel) {
                                                      return IKernel(KERNEL_POSITION.getStorageAddress());
                                                  }
                                              
                                                  function appId() public view returns (bytes32) {
                                                      return APP_ID_POSITION.getStorageBytes32();
                                                  }
                                              
                                                  function setKernel(IKernel _kernel) internal {
                                                      KERNEL_POSITION.setStorageAddress(address(_kernel));
                                                  }
                                              
                                                  function setAppId(bytes32 _appId) internal {
                                                      APP_ID_POSITION.setStorageBytes32(_appId);
                                                  }
                                              }
                                              
                                              // File: contracts/lib/misc/ERCProxy.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              contract ERCProxy {
                                                  uint256 internal constant FORWARDING = 1;
                                                  uint256 internal constant UPGRADEABLE = 2;
                                              
                                                  function proxyType() public pure returns (uint256 proxyTypeId);
                                                  function implementation() public view returns (address codeAddr);
                                              }
                                              
                                              // File: contracts/common/DelegateProxy.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              contract DelegateProxy is ERCProxy, IsContract {
                                                  uint256 internal constant FWD_GAS_LIMIT = 10000;
                                              
                                                  /**
                                                  * @dev Performs a delegatecall and returns whatever the delegatecall returned (entire context execution will return!)
                                                  * @param _dst Destination address to perform the delegatecall
                                                  * @param _calldata Calldata for the delegatecall
                                                  */
                                                  function delegatedFwd(address _dst, bytes _calldata) internal {
                                                      require(isContract(_dst));
                                                      uint256 fwdGasLimit = FWD_GAS_LIMIT;
                                              
                                                      assembly {
                                                          let result := delegatecall(sub(gas, fwdGasLimit), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0)
                                                          let size := returndatasize
                                                          let ptr := mload(0x40)
                                                          returndatacopy(ptr, 0, size)
                                              
                                                          // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
                                                          // if the call returned error data, forward it
                                                          switch result case 0 { revert(ptr, size) }
                                                          default { return(ptr, size) }
                                                      }
                                                  }
                                              }
                                              
                                              // File: contracts/common/DepositableStorage.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              contract DepositableStorage {
                                                  using UnstructuredStorage for bytes32;
                                              
                                                  // keccak256("aragonOS.depositableStorage.depositable")
                                                  bytes32 internal constant DEPOSITABLE_POSITION = 0x665fd576fbbe6f247aff98f5c94a561e3f71ec2d3c988d56f12d342396c50cea;
                                              
                                                  function isDepositable() public view returns (bool) {
                                                      return DEPOSITABLE_POSITION.getStorageBool();
                                                  }
                                              
                                                  function setDepositable(bool _depositable) internal {
                                                      DEPOSITABLE_POSITION.setStorageBool(_depositable);
                                                  }
                                              }
                                              
                                              // File: contracts/common/DepositableDelegateProxy.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              contract DepositableDelegateProxy is DepositableStorage, DelegateProxy {
                                                  event ProxyDeposit(address sender, uint256 value);
                                              
                                                  function () external payable {
                                                      uint256 forwardGasThreshold = FWD_GAS_LIMIT;
                                                      bytes32 isDepositablePosition = DEPOSITABLE_POSITION;
                                              
                                                      // Optimized assembly implementation to prevent EIP-1884 from breaking deposits, reference code in Solidity:
                                                      // https://github.com/aragon/aragonOS/blob/v4.2.1/contracts/common/DepositableDelegateProxy.sol#L10-L20
                                                      assembly {
                                                          // Continue only if the gas left is lower than the threshold for forwarding to the implementation code,
                                                          // otherwise continue outside of the assembly block.
                                                          if lt(gas, forwardGasThreshold) {
                                                              // Only accept the deposit and emit an event if all of the following are true:
                                                              // the proxy accepts deposits (isDepositable), msg.data.length == 0, and msg.value > 0
                                                              if and(and(sload(isDepositablePosition), iszero(calldatasize)), gt(callvalue, 0)) {
                                                                  // Equivalent Solidity code for emitting the event:
                                                                  // emit ProxyDeposit(msg.sender, msg.value);
                                              
                                                                  let logData := mload(0x40) // free memory pointer
                                                                  mstore(logData, caller) // add 'msg.sender' to the log data (first event param)
                                                                  mstore(add(logData, 0x20), callvalue) // add 'msg.value' to the log data (second event param)
                                              
                                                                  // Emit an event with one topic to identify the event: keccak256('ProxyDeposit(address,uint256)') = 0x15ee...dee1
                                                                  log1(logData, 0x40, 0x15eeaa57c7bd188c1388020bcadc2c436ec60d647d36ef5b9eb3c742217ddee1)
                                              
                                                                  stop() // Stop. Exits execution context
                                                              }
                                              
                                                              // If any of above checks failed, revert the execution (if ETH was sent, it is returned to the sender)
                                                              revert(0, 0)
                                                          }
                                                      }
                                              
                                                      address target = implementation();
                                                      delegatedFwd(target, msg.data);
                                                  }
                                              }
                                              
                                              // File: contracts/apps/AppProxyBase.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              contract AppProxyBase is AppStorage, DepositableDelegateProxy, KernelNamespaceConstants {
                                                  /**
                                                  * @dev Initialize AppProxy
                                                  * @param _kernel Reference to organization kernel for the app
                                                  * @param _appId Identifier for app
                                                  * @param _initializePayload Payload for call to be made after setup to initialize
                                                  */
                                                  constructor(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public {
                                                      setKernel(_kernel);
                                                      setAppId(_appId);
                                              
                                                      // Implicit check that kernel is actually a Kernel
                                                      // The EVM doesn't actually provide a way for us to make sure, but we can force a revert to
                                                      // occur if the kernel is set to 0x0 or a non-code address when we try to call a method on
                                                      // it.
                                                      address appCode = getAppBase(_appId);
                                              
                                                      // If initialize payload is provided, it will be executed
                                                      if (_initializePayload.length > 0) {
                                                          require(isContract(appCode));
                                                          // Cannot make delegatecall as a delegateproxy.delegatedFwd as it
                                                          // returns ending execution context and halts contract deployment
                                                          require(appCode.delegatecall(_initializePayload));
                                                      }
                                                  }
                                              
                                                  function getAppBase(bytes32 _appId) internal view returns (address) {
                                                      return kernel().getApp(KERNEL_APP_BASES_NAMESPACE, _appId);
                                                  }
                                              }
                                              
                                              // File: contracts/apps/AppProxyUpgradeable.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              contract AppProxyUpgradeable is AppProxyBase {
                                                  /**
                                                  * @dev Initialize AppProxyUpgradeable (makes it an upgradeable Aragon app)
                                                  * @param _kernel Reference to organization kernel for the app
                                                  * @param _appId Identifier for app
                                                  * @param _initializePayload Payload for call to be made after setup to initialize
                                                  */
                                                  constructor(IKernel _kernel, bytes32 _appId, bytes _initializePayload)
                                                      AppProxyBase(_kernel, _appId, _initializePayload)
                                                      public // solium-disable-line visibility-first
                                                  {
                                                      // solium-disable-previous-line no-empty-blocks
                                                  }
                                              
                                                  /**
                                                   * @dev ERC897, the address the proxy would delegate calls to
                                                   */
                                                  function implementation() public view returns (address) {
                                                      return getAppBase(appId());
                                                  }
                                              
                                                  /**
                                                   * @dev ERC897, whether it is a forwarding (1) or an upgradeable (2) proxy
                                                   */
                                                  function proxyType() public pure returns (uint256 proxyTypeId) {
                                                      return UPGRADEABLE;
                                                  }
                                              }
                                              
                                              // File: contracts/apps/AppProxyPinned.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              contract AppProxyPinned is IsContract, AppProxyBase {
                                                  using UnstructuredStorage for bytes32;
                                              
                                                  // keccak256("aragonOS.appStorage.pinnedCode")
                                                  bytes32 internal constant PINNED_CODE_POSITION = 0xdee64df20d65e53d7f51cb6ab6d921a0a6a638a91e942e1d8d02df28e31c038e;
                                              
                                                  /**
                                                  * @dev Initialize AppProxyPinned (makes it an un-upgradeable Aragon app)
                                                  * @param _kernel Reference to organization kernel for the app
                                                  * @param _appId Identifier for app
                                                  * @param _initializePayload Payload for call to be made after setup to initialize
                                                  */
                                                  constructor(IKernel _kernel, bytes32 _appId, bytes _initializePayload)
                                                      AppProxyBase(_kernel, _appId, _initializePayload)
                                                      public // solium-disable-line visibility-first
                                                  {
                                                      setPinnedCode(getAppBase(_appId));
                                                      require(isContract(pinnedCode()));
                                                  }
                                              
                                                  /**
                                                   * @dev ERC897, the address the proxy would delegate calls to
                                                   */
                                                  function implementation() public view returns (address) {
                                                      return pinnedCode();
                                                  }
                                              
                                                  /**
                                                   * @dev ERC897, whether it is a forwarding (1) or an upgradeable (2) proxy
                                                   */
                                                  function proxyType() public pure returns (uint256 proxyTypeId) {
                                                      return FORWARDING;
                                                  }
                                              
                                                  function setPinnedCode(address _pinnedCode) internal {
                                                      PINNED_CODE_POSITION.setStorageAddress(_pinnedCode);
                                                  }
                                              
                                                  function pinnedCode() internal view returns (address) {
                                                      return PINNED_CODE_POSITION.getStorageAddress();
                                                  }
                                              }
                                              
                                              // File: contracts/factory/AppProxyFactory.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              contract AppProxyFactory {
                                                  event NewAppProxy(address proxy, bool isUpgradeable, bytes32 appId);
                                              
                                                  /**
                                                  * @notice Create a new upgradeable app instance on `_kernel` with identifier `_appId`
                                                  * @param _kernel App's Kernel reference
                                                  * @param _appId Identifier for app
                                                  * @return AppProxyUpgradeable
                                                  */
                                                  function newAppProxy(IKernel _kernel, bytes32 _appId) public returns (AppProxyUpgradeable) {
                                                      return newAppProxy(_kernel, _appId, new bytes(0));
                                                  }
                                              
                                                  /**
                                                  * @notice Create a new upgradeable app instance on `_kernel` with identifier `_appId` and initialization payload `_initializePayload`
                                                  * @param _kernel App's Kernel reference
                                                  * @param _appId Identifier for app
                                                  * @return AppProxyUpgradeable
                                                  */
                                                  function newAppProxy(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public returns (AppProxyUpgradeable) {
                                                      AppProxyUpgradeable proxy = new AppProxyUpgradeable(_kernel, _appId, _initializePayload);
                                                      emit NewAppProxy(address(proxy), true, _appId);
                                                      return proxy;
                                                  }
                                              
                                                  /**
                                                  * @notice Create a new pinned app instance on `_kernel` with identifier `_appId`
                                                  * @param _kernel App's Kernel reference
                                                  * @param _appId Identifier for app
                                                  * @return AppProxyPinned
                                                  */
                                                  function newAppProxyPinned(IKernel _kernel, bytes32 _appId) public returns (AppProxyPinned) {
                                                      return newAppProxyPinned(_kernel, _appId, new bytes(0));
                                                  }
                                              
                                                  /**
                                                  * @notice Create a new pinned app instance on `_kernel` with identifier `_appId` and initialization payload `_initializePayload`
                                                  * @param _kernel App's Kernel reference
                                                  * @param _appId Identifier for app
                                                  * @param _initializePayload Proxy initialization payload
                                                  * @return AppProxyPinned
                                                  */
                                                  function newAppProxyPinned(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public returns (AppProxyPinned) {
                                                      AppProxyPinned proxy = new AppProxyPinned(_kernel, _appId, _initializePayload);
                                                      emit NewAppProxy(address(proxy), false, _appId);
                                                      return proxy;
                                                  }
                                              }
                                              
                                              // File: contracts/kernel/Kernel.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              // solium-disable-next-line max-len
                                              contract Kernel is IKernel, KernelStorage, KernelAppIds, KernelNamespaceConstants, Petrifiable, IsContract, VaultRecoverable, AppProxyFactory, ACLSyntaxSugar {
                                                  /* Hardcoded constants to save gas
                                                  bytes32 public constant APP_MANAGER_ROLE = keccak256("APP_MANAGER_ROLE");
                                                  */
                                                  bytes32 public constant APP_MANAGER_ROLE = 0xb6d92708f3d4817afc106147d969e229ced5c46e65e0a5002a0d391287762bd0;
                                              
                                                  string private constant ERROR_APP_NOT_CONTRACT = "KERNEL_APP_NOT_CONTRACT";
                                                  string private constant ERROR_INVALID_APP_CHANGE = "KERNEL_INVALID_APP_CHANGE";
                                                  string private constant ERROR_AUTH_FAILED = "KERNEL_AUTH_FAILED";
                                              
                                                  /**
                                                  * @dev Constructor that allows the deployer to choose if the base instance should be petrified immediately.
                                                  * @param _shouldPetrify Immediately petrify this instance so that it can never be initialized
                                                  */
                                                  constructor(bool _shouldPetrify) public {
                                                      if (_shouldPetrify) {
                                                          petrify();
                                                      }
                                                  }
                                              
                                                  /**
                                                  * @dev Initialize can only be called once. It saves the block number in which it was initialized.
                                                  * @notice Initialize this kernel instance along with its ACL and set `_permissionsCreator` as the entity that can create other permissions
                                                  * @param _baseAcl Address of base ACL app
                                                  * @param _permissionsCreator Entity that will be given permission over createPermission
                                                  */
                                                  function initialize(IACL _baseAcl, address _permissionsCreator) public onlyInit {
                                                      initialized();
                                              
                                                      // Set ACL base
                                                      _setApp(KERNEL_APP_BASES_NAMESPACE, KERNEL_DEFAULT_ACL_APP_ID, _baseAcl);
                                              
                                                      // Create ACL instance and attach it as the default ACL app
                                                      IACL acl = IACL(newAppProxy(this, KERNEL_DEFAULT_ACL_APP_ID));
                                                      acl.initialize(_permissionsCreator);
                                                      _setApp(KERNEL_APP_ADDR_NAMESPACE, KERNEL_DEFAULT_ACL_APP_ID, acl);
                                              
                                                      recoveryVaultAppId = KERNEL_DEFAULT_VAULT_APP_ID;
                                                  }
                                              
                                                  /**
                                                  * @dev Create a new instance of an app linked to this kernel
                                                  * @notice Create a new upgradeable instance of `_appId` app linked to the Kernel, setting its code to `_appBase`
                                                  * @param _appId Identifier for app
                                                  * @param _appBase Address of the app's base implementation
                                                  * @return AppProxy instance
                                                  */
                                                  function newAppInstance(bytes32 _appId, address _appBase)
                                                      public
                                                      auth(APP_MANAGER_ROLE, arr(KERNEL_APP_BASES_NAMESPACE, _appId))
                                                      returns (ERCProxy appProxy)
                                                  {
                                                      return newAppInstance(_appId, _appBase, new bytes(0), false);
                                                  }
                                              
                                                  /**
                                                  * @dev Create a new instance of an app linked to this kernel and set its base
                                                  *      implementation if it was not already set
                                                  * @notice Create a new upgradeable instance of `_appId` app linked to the Kernel, setting its code to `_appBase`. `_setDefault ? 'Also sets it as the default app instance.':''`
                                                  * @param _appId Identifier for app
                                                  * @param _appBase Address of the app's base implementation
                                                  * @param _initializePayload Payload for call made by the proxy during its construction to initialize
                                                  * @param _setDefault Whether the app proxy app is the default one.
                                                  *        Useful when the Kernel needs to know of an instance of a particular app,
                                                  *        like Vault for escape hatch mechanism.
                                                  * @return AppProxy instance
                                                  */
                                                  function newAppInstance(bytes32 _appId, address _appBase, bytes _initializePayload, bool _setDefault)
                                                      public
                                                      auth(APP_MANAGER_ROLE, arr(KERNEL_APP_BASES_NAMESPACE, _appId))
                                                      returns (ERCProxy appProxy)
                                                  {
                                                      _setAppIfNew(KERNEL_APP_BASES_NAMESPACE, _appId, _appBase);
                                                      appProxy = newAppProxy(this, _appId, _initializePayload);
                                                      // By calling setApp directly and not the internal functions, we make sure the params are checked
                                                      // and it will only succeed if sender has permissions to set something to the namespace.
                                                      if (_setDefault) {
                                                          setApp(KERNEL_APP_ADDR_NAMESPACE, _appId, appProxy);
                                                      }
                                                  }
                                              
                                                  /**
                                                  * @dev Create a new pinned instance of an app linked to this kernel
                                                  * @notice Create a new non-upgradeable instance of `_appId` app linked to the Kernel, setting its code to `_appBase`.
                                                  * @param _appId Identifier for app
                                                  * @param _appBase Address of the app's base implementation
                                                  * @return AppProxy instance
                                                  */
                                                  function newPinnedAppInstance(bytes32 _appId, address _appBase)
                                                      public
                                                      auth(APP_MANAGER_ROLE, arr(KERNEL_APP_BASES_NAMESPACE, _appId))
                                                      returns (ERCProxy appProxy)
                                                  {
                                                      return newPinnedAppInstance(_appId, _appBase, new bytes(0), false);
                                                  }
                                              
                                                  /**
                                                  * @dev Create a new pinned instance of an app linked to this kernel and set
                                                  *      its base implementation if it was not already set
                                                  * @notice Create a new non-upgradeable instance of `_appId` app linked to the Kernel, setting its code to `_appBase`. `_setDefault ? 'Also sets it as the default app instance.':''`
                                                  * @param _appId Identifier for app
                                                  * @param _appBase Address of the app's base implementation
                                                  * @param _initializePayload Payload for call made by the proxy during its construction to initialize
                                                  * @param _setDefault Whether the app proxy app is the default one.
                                                  *        Useful when the Kernel needs to know of an instance of a particular app,
                                                  *        like Vault for escape hatch mechanism.
                                                  * @return AppProxy instance
                                                  */
                                                  function newPinnedAppInstance(bytes32 _appId, address _appBase, bytes _initializePayload, bool _setDefault)
                                                      public
                                                      auth(APP_MANAGER_ROLE, arr(KERNEL_APP_BASES_NAMESPACE, _appId))
                                                      returns (ERCProxy appProxy)
                                                  {
                                                      _setAppIfNew(KERNEL_APP_BASES_NAMESPACE, _appId, _appBase);
                                                      appProxy = newAppProxyPinned(this, _appId, _initializePayload);
                                                      // By calling setApp directly and not the internal functions, we make sure the params are checked
                                                      // and it will only succeed if sender has permissions to set something to the namespace.
                                                      if (_setDefault) {
                                                          setApp(KERNEL_APP_ADDR_NAMESPACE, _appId, appProxy);
                                                      }
                                                  }
                                              
                                                  /**
                                                  * @dev Set the resolving address of an app instance or base implementation
                                                  * @notice Set the resolving address of `_appId` in namespace `_namespace` to `_app`
                                                  * @param _namespace App namespace to use
                                                  * @param _appId Identifier for app
                                                  * @param _app Address of the app instance or base implementation
                                                  * @return ID of app
                                                  */
                                                  function setApp(bytes32 _namespace, bytes32 _appId, address _app)
                                                      public
                                                      auth(APP_MANAGER_ROLE, arr(_namespace, _appId))
                                                  {
                                                      _setApp(_namespace, _appId, _app);
                                                  }
                                              
                                                  /**
                                                  * @dev Set the default vault id for the escape hatch mechanism
                                                  * @param _recoveryVaultAppId Identifier of the recovery vault app
                                                  */
                                                  function setRecoveryVaultAppId(bytes32 _recoveryVaultAppId)
                                                      public
                                                      auth(APP_MANAGER_ROLE, arr(KERNEL_APP_ADDR_NAMESPACE, _recoveryVaultAppId))
                                                  {
                                                      recoveryVaultAppId = _recoveryVaultAppId;
                                                  }
                                              
                                                  // External access to default app id and namespace constants to mimic default getters for constants
                                                  /* solium-disable function-order, mixedcase */
                                                  function CORE_NAMESPACE() external pure returns (bytes32) { return KERNEL_CORE_NAMESPACE; }
                                                  function APP_BASES_NAMESPACE() external pure returns (bytes32) { return KERNEL_APP_BASES_NAMESPACE; }
                                                  function APP_ADDR_NAMESPACE() external pure returns (bytes32) { return KERNEL_APP_ADDR_NAMESPACE; }
                                                  function KERNEL_APP_ID() external pure returns (bytes32) { return KERNEL_CORE_APP_ID; }
                                                  function DEFAULT_ACL_APP_ID() external pure returns (bytes32) { return KERNEL_DEFAULT_ACL_APP_ID; }
                                                  /* solium-enable function-order, mixedcase */
                                              
                                                  /**
                                                  * @dev Get the address of an app instance or base implementation
                                                  * @param _namespace App namespace to use
                                                  * @param _appId Identifier for app
                                                  * @return Address of the app
                                                  */
                                                  function getApp(bytes32 _namespace, bytes32 _appId) public view returns (address) {
                                                      return apps[_namespace][_appId];
                                                  }
                                              
                                                  /**
                                                  * @dev Get the address of the recovery Vault instance (to recover funds)
                                                  * @return Address of the Vault
                                                  */
                                                  function getRecoveryVault() public view returns (address) {
                                                      return apps[KERNEL_APP_ADDR_NAMESPACE][recoveryVaultAppId];
                                                  }
                                              
                                                  /**
                                                  * @dev Get the installed ACL app
                                                  * @return ACL app
                                                  */
                                                  function acl() public view returns (IACL) {
                                                      return IACL(getApp(KERNEL_APP_ADDR_NAMESPACE, KERNEL_DEFAULT_ACL_APP_ID));
                                                  }
                                              
                                                  /**
                                                  * @dev Function called by apps to check ACL on kernel or to check permission status
                                                  * @param _who Sender of the original call
                                                  * @param _where Address of the app
                                                  * @param _what Identifier for a group of actions in app
                                                  * @param _how Extra data for ACL auth
                                                  * @return Boolean indicating whether the ACL allows the role or not.
                                                  *         Always returns false if the kernel hasn't been initialized yet.
                                                  */
                                                  function hasPermission(address _who, address _where, bytes32 _what, bytes _how) public view returns (bool) {
                                                      IACL defaultAcl = acl();
                                                      return address(defaultAcl) != address(0) && // Poor man's initialization check (saves gas)
                                                          defaultAcl.hasPermission(_who, _where, _what, _how);
                                                  }
                                              
                                                  function _setApp(bytes32 _namespace, bytes32 _appId, address _app) internal {
                                                      require(isContract(_app), ERROR_APP_NOT_CONTRACT);
                                                      apps[_namespace][_appId] = _app;
                                                      emit SetApp(_namespace, _appId, _app);
                                                  }
                                              
                                                  function _setAppIfNew(bytes32 _namespace, bytes32 _appId, address _app) internal {
                                                      address app = getApp(_namespace, _appId);
                                                      if (app != address(0)) {
                                                          // The only way to set an app is if it passes the isContract check, so no need to check it again
                                                          require(app == _app, ERROR_INVALID_APP_CHANGE);
                                                      } else {
                                                          _setApp(_namespace, _appId, _app);
                                                      }
                                                  }
                                              
                                                  modifier auth(bytes32 _role, uint256[] memory _params) {
                                                      require(
                                                          hasPermission(msg.sender, address(this), _role, ConversionHelpers.dangerouslyCastUintArrayToBytes(_params)),
                                                          ERROR_AUTH_FAILED
                                                      );
                                                      _;
                                                  }
                                              }
                                              
                                              // File: contracts/kernel/KernelProxy.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              contract KernelProxy is IKernelEvents, KernelStorage, KernelAppIds, KernelNamespaceConstants, IsContract, DepositableDelegateProxy {
                                                  /**
                                                  * @dev KernelProxy is a proxy contract to a kernel implementation. The implementation
                                                  *      can update the reference, which effectively upgrades the contract
                                                  * @param _kernelImpl Address of the contract used as implementation for kernel
                                                  */
                                                  constructor(IKernel _kernelImpl) public {
                                                      require(isContract(address(_kernelImpl)));
                                                      apps[KERNEL_CORE_NAMESPACE][KERNEL_CORE_APP_ID] = _kernelImpl;
                                              
                                                      // Note that emitting this event is important for verifying that a KernelProxy instance
                                                      // was never upgraded to a malicious Kernel logic contract over its lifespan.
                                                      // This starts the "chain of trust", that can be followed through later SetApp() events
                                                      // emitted during kernel upgrades.
                                                      emit SetApp(KERNEL_CORE_NAMESPACE, KERNEL_CORE_APP_ID, _kernelImpl);
                                                  }
                                              
                                                  /**
                                                   * @dev ERC897, whether it is a forwarding (1) or an upgradeable (2) proxy
                                                   */
                                                  function proxyType() public pure returns (uint256 proxyTypeId) {
                                                      return UPGRADEABLE;
                                                  }
                                              
                                                  /**
                                                  * @dev ERC897, the address the proxy would delegate calls to
                                                  */
                                                  function implementation() public view returns (address) {
                                                      return apps[KERNEL_CORE_NAMESPACE][KERNEL_CORE_APP_ID];
                                                  }
                                              }
                                              
                                              // File: contracts/common/Autopetrified.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              contract Autopetrified is Petrifiable {
                                                  constructor() public {
                                                      // Immediately petrify base (non-proxy) instances of inherited contracts on deploy.
                                                      // This renders them uninitializable (and unusable without a proxy).
                                                      petrify();
                                                  }
                                              }
                                              
                                              // File: contracts/common/ReentrancyGuard.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              contract ReentrancyGuard {
                                                  using UnstructuredStorage for bytes32;
                                              
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant REENTRANCY_MUTEX_POSITION = keccak256("aragonOS.reentrancyGuard.mutex");
                                                  */
                                                  bytes32 private constant REENTRANCY_MUTEX_POSITION = 0xe855346402235fdd185c890e68d2c4ecad599b88587635ee285bce2fda58dacb;
                                              
                                                  string private constant ERROR_REENTRANT = "REENTRANCY_REENTRANT_CALL";
                                              
                                                  modifier nonReentrant() {
                                                      // Ensure mutex is unlocked
                                                      require(!REENTRANCY_MUTEX_POSITION.getStorageBool(), ERROR_REENTRANT);
                                              
                                                      // Lock mutex before function call
                                                      REENTRANCY_MUTEX_POSITION.setStorageBool(true);
                                              
                                                      // Perform function call
                                                      _;
                                              
                                                      // Unlock mutex after function call
                                                      REENTRANCY_MUTEX_POSITION.setStorageBool(false);
                                                  }
                                              }
                                              
                                              // File: contracts/evmscript/IEVMScriptExecutor.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              interface IEVMScriptExecutor {
                                                  function execScript(bytes script, bytes input, address[] blacklist) external returns (bytes);
                                                  function executorType() external pure returns (bytes32);
                                              }
                                              
                                              // File: contracts/evmscript/IEVMScriptRegistry.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              contract EVMScriptRegistryConstants {
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant EVMSCRIPT_REGISTRY_APP_ID = apmNamehash("evmreg");
                                                  */
                                                  bytes32 internal constant EVMSCRIPT_REGISTRY_APP_ID = 0xddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd61;
                                              }
                                              
                                              
                                              interface IEVMScriptRegistry {
                                                  function addScriptExecutor(IEVMScriptExecutor executor) external returns (uint id);
                                                  function disableScriptExecutor(uint256 executorId) external;
                                              
                                                  // TODO: this should be external
                                                  // See https://github.com/ethereum/solidity/issues/4832
                                                  function getScriptExecutor(bytes script) public view returns (IEVMScriptExecutor);
                                              }
                                              
                                              // File: contracts/evmscript/EVMScriptRunner.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              contract EVMScriptRunner is AppStorage, Initializable, EVMScriptRegistryConstants, KernelNamespaceConstants {
                                                  string private constant ERROR_EXECUTOR_UNAVAILABLE = "EVMRUN_EXECUTOR_UNAVAILABLE";
                                                  string private constant ERROR_PROTECTED_STATE_MODIFIED = "EVMRUN_PROTECTED_STATE_MODIFIED";
                                              
                                                  /* This is manually crafted in assembly
                                                  string private constant ERROR_EXECUTOR_INVALID_RETURN = "EVMRUN_EXECUTOR_INVALID_RETURN";
                                                  */
                                              
                                                  event ScriptResult(address indexed executor, bytes script, bytes input, bytes returnData);
                                              
                                                  function getEVMScriptExecutor(bytes _script) public view returns (IEVMScriptExecutor) {
                                                      return IEVMScriptExecutor(getEVMScriptRegistry().getScriptExecutor(_script));
                                                  }
                                              
                                                  function getEVMScriptRegistry() public view returns (IEVMScriptRegistry) {
                                                      address registryAddr = kernel().getApp(KERNEL_APP_ADDR_NAMESPACE, EVMSCRIPT_REGISTRY_APP_ID);
                                                      return IEVMScriptRegistry(registryAddr);
                                                  }
                                              
                                                  function runScript(bytes _script, bytes _input, address[] _blacklist)
                                                      internal
                                                      isInitialized
                                                      protectState
                                                      returns (bytes)
                                                  {
                                                      IEVMScriptExecutor executor = getEVMScriptExecutor(_script);
                                                      require(address(executor) != address(0), ERROR_EXECUTOR_UNAVAILABLE);
                                              
                                                      bytes4 sig = executor.execScript.selector;
                                                      bytes memory data = abi.encodeWithSelector(sig, _script, _input, _blacklist);
                                              
                                                      bytes memory output;
                                                      assembly {
                                                          let success := delegatecall(
                                                              gas,                // forward all gas
                                                              executor,           // address
                                                              add(data, 0x20),    // calldata start
                                                              mload(data),        // calldata length
                                                              0,                  // don't write output (we'll handle this ourselves)
                                                              0                   // don't write output
                                                          )
                                              
                                                          output := mload(0x40) // free mem ptr get
                                              
                                                          switch success
                                                          case 0 {
                                                              // If the call errored, forward its full error data
                                                              returndatacopy(output, 0, returndatasize)
                                                              revert(output, returndatasize)
                                                          }
                                                          default {
                                                              switch gt(returndatasize, 0x3f)
                                                              case 0 {
                                                                  // Need at least 0x40 bytes returned for properly ABI-encoded bytes values,
                                                                  // revert with "EVMRUN_EXECUTOR_INVALID_RETURN"
                                                                  // See remix: doing a `revert("EVMRUN_EXECUTOR_INVALID_RETURN")` always results in
                                                                  // this memory layout
                                                                  mstore(output, 0x08c379a000000000000000000000000000000000000000000000000000000000)         // error identifier
                                                                  mstore(add(output, 0x04), 0x0000000000000000000000000000000000000000000000000000000000000020) // starting offset
                                                                  mstore(add(output, 0x24), 0x000000000000000000000000000000000000000000000000000000000000001e) // reason length
                                                                  mstore(add(output, 0x44), 0x45564d52554e5f4558454355544f525f494e56414c49445f52455455524e0000) // reason
                                              
                                                                  revert(output, 100) // 100 = 4 + 3 * 32 (error identifier + 3 words for the ABI encoded error)
                                                              }
                                                              default {
                                                                  // Copy result
                                                                  //
                                                                  // Needs to perform an ABI decode for the expected `bytes` return type of
                                                                  // `executor.execScript()` as solidity will automatically ABI encode the returned bytes as:
                                                                  //    [ position of the first dynamic length return value = 0x20 (32 bytes) ]
                                                                  //    [ output length (32 bytes) ]
                                                                  //    [ output content (N bytes) ]
                                                                  //
                                                                  // Perform the ABI decode by ignoring the first 32 bytes of the return data
                                                                  let copysize := sub(returndatasize, 0x20)
                                                                  returndatacopy(output, 0x20, copysize)
                                              
                                                                  mstore(0x40, add(output, copysize)) // free mem ptr set
                                                              }
                                                          }
                                                      }
                                              
                                                      emit ScriptResult(address(executor), _script, _input, output);
                                              
                                                      return output;
                                                  }
                                              
                                                  modifier protectState {
                                                      address preKernel = address(kernel());
                                                      bytes32 preAppId = appId();
                                                      _; // exec
                                                      require(address(kernel()) == preKernel, ERROR_PROTECTED_STATE_MODIFIED);
                                                      require(appId() == preAppId, ERROR_PROTECTED_STATE_MODIFIED);
                                                  }
                                              }
                                              
                                              // File: contracts/apps/AragonApp.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              // Contracts inheriting from AragonApp are, by default, immediately petrified upon deployment so
                                              // that they can never be initialized.
                                              // Unless overriden, this behaviour enforces those contracts to be usable only behind an AppProxy.
                                              // ReentrancyGuard, EVMScriptRunner, and ACLSyntaxSugar are not directly used by this contract, but
                                              // are included so that they are automatically usable by subclassing contracts
                                              contract AragonApp is AppStorage, Autopetrified, VaultRecoverable, ReentrancyGuard, EVMScriptRunner, ACLSyntaxSugar {
                                                  string private constant ERROR_AUTH_FAILED = "APP_AUTH_FAILED";
                                              
                                                  modifier auth(bytes32 _role) {
                                                      require(canPerform(msg.sender, _role, new uint256[](0)), ERROR_AUTH_FAILED);
                                                      _;
                                                  }
                                              
                                                  modifier authP(bytes32 _role, uint256[] _params) {
                                                      require(canPerform(msg.sender, _role, _params), ERROR_AUTH_FAILED);
                                                      _;
                                                  }
                                              
                                                  /**
                                                  * @dev Check whether an action can be performed by a sender for a particular role on this app
                                                  * @param _sender Sender of the call
                                                  * @param _role Role on this app
                                                  * @param _params Permission params for the role
                                                  * @return Boolean indicating whether the sender has the permissions to perform the action.
                                                  *         Always returns false if the app hasn't been initialized yet.
                                                  */
                                                  function canPerform(address _sender, bytes32 _role, uint256[] _params) public view returns (bool) {
                                                      if (!hasInitialized()) {
                                                          return false;
                                                      }
                                              
                                                      IKernel linkedKernel = kernel();
                                                      if (address(linkedKernel) == address(0)) {
                                                          return false;
                                                      }
                                              
                                                      return linkedKernel.hasPermission(
                                                          _sender,
                                                          address(this),
                                                          _role,
                                                          ConversionHelpers.dangerouslyCastUintArrayToBytes(_params)
                                                      );
                                                  }
                                              
                                                  /**
                                                  * @dev Get the recovery vault for the app
                                                  * @return Recovery vault address for the app
                                                  */
                                                  function getRecoveryVault() public view returns (address) {
                                                      // Funds recovery via a vault is only available when used with a kernel
                                                      return kernel().getRecoveryVault(); // if kernel is not set, it will revert
                                                  }
                                              }
                                              
                                              // File: contracts/acl/IACLOracle.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              interface IACLOracle {
                                                  function canPerform(address who, address where, bytes32 what, uint256[] how) external view returns (bool);
                                              }
                                              
                                              // File: contracts/acl/ACL.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              /* solium-disable function-order */
                                              // Allow public initialize() to be first
                                              contract ACL is IACL, TimeHelpers, AragonApp, ACLHelpers {
                                                  /* Hardcoded constants to save gas
                                                  bytes32 public constant CREATE_PERMISSIONS_ROLE = keccak256("CREATE_PERMISSIONS_ROLE");
                                                  */
                                                  bytes32 public constant CREATE_PERMISSIONS_ROLE = 0x0b719b33c83b8e5d300c521cb8b54ae9bd933996a14bef8c2f4e0285d2d2400a;
                                              
                                                  enum Op { NONE, EQ, NEQ, GT, LT, GTE, LTE, RET, NOT, AND, OR, XOR, IF_ELSE } // op types
                                              
                                                  struct Param {
                                                      uint8 id;
                                                      uint8 op;
                                                      uint240 value; // even though value is an uint240 it can store addresses
                                                      // in the case of 32 byte hashes losing 2 bytes precision isn't a huge deal
                                                      // op and id take less than 1 byte each so it can be kept in 1 sstore
                                                  }
                                              
                                                  uint8 internal constant BLOCK_NUMBER_PARAM_ID = 200;
                                                  uint8 internal constant TIMESTAMP_PARAM_ID    = 201;
                                                  // 202 is unused
                                                  uint8 internal constant ORACLE_PARAM_ID       = 203;
                                                  uint8 internal constant LOGIC_OP_PARAM_ID     = 204;
                                                  uint8 internal constant PARAM_VALUE_PARAM_ID  = 205;
                                                  // TODO: Add execution times param type?
                                              
                                                  /* Hardcoded constant to save gas
                                                  bytes32 public constant EMPTY_PARAM_HASH = keccak256(uint256(0));
                                                  */
                                                  bytes32 public constant EMPTY_PARAM_HASH = 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563;
                                                  bytes32 public constant NO_PERMISSION = bytes32(0);
                                                  address public constant ANY_ENTITY = address(-1);
                                                  address public constant BURN_ENTITY = address(1); // address(0) is already used as "no permission manager"
                                              
                                                  string private constant ERROR_AUTH_INIT_KERNEL = "ACL_AUTH_INIT_KERNEL";
                                                  string private constant ERROR_AUTH_NO_MANAGER = "ACL_AUTH_NO_MANAGER";
                                                  string private constant ERROR_EXISTENT_MANAGER = "ACL_EXISTENT_MANAGER";
                                              
                                                  // Whether someone has a permission
                                                  mapping (bytes32 => bytes32) internal permissions; // permissions hash => params hash
                                                  mapping (bytes32 => Param[]) internal permissionParams; // params hash => params
                                              
                                                  // Who is the manager of a permission
                                                  mapping (bytes32 => address) internal permissionManager;
                                              
                                                  event SetPermission(address indexed entity, address indexed app, bytes32 indexed role, bool allowed);
                                                  event SetPermissionParams(address indexed entity, address indexed app, bytes32 indexed role, bytes32 paramsHash);
                                                  event ChangePermissionManager(address indexed app, bytes32 indexed role, address indexed manager);
                                              
                                                  modifier onlyPermissionManager(address _app, bytes32 _role) {
                                                      require(msg.sender == getPermissionManager(_app, _role), ERROR_AUTH_NO_MANAGER);
                                                      _;
                                                  }
                                              
                                                  modifier noPermissionManager(address _app, bytes32 _role) {
                                                      // only allow permission creation (or re-creation) when there is no manager
                                                      require(getPermissionManager(_app, _role) == address(0), ERROR_EXISTENT_MANAGER);
                                                      _;
                                                  }
                                              
                                                  /**
                                                  * @dev Initialize can only be called once. It saves the block number in which it was initialized.
                                                  * @notice Initialize an ACL instance and set `_permissionsCreator` as the entity that can create other permissions
                                                  * @param _permissionsCreator Entity that will be given permission over createPermission
                                                  */
                                                  function initialize(address _permissionsCreator) public onlyInit {
                                                      initialized();
                                                      require(msg.sender == address(kernel()), ERROR_AUTH_INIT_KERNEL);
                                              
                                                      _createPermission(_permissionsCreator, this, CREATE_PERMISSIONS_ROLE, _permissionsCreator);
                                                  }
                                              
                                                  /**
                                                  * @dev Creates a permission that wasn't previously set and managed.
                                                  *      If a created permission is removed it is possible to reset it with createPermission.
                                                  *      This is the **ONLY** way to create permissions and set managers to permissions that don't
                                                  *      have a manager.
                                                  *      In terms of the ACL being initialized, this function implicitly protects all the other
                                                  *      state-changing external functions, as they all require the sender to be a manager.
                                                  * @notice Create a new permission granting `_entity` the ability to perform actions requiring `_role` on `_app`, setting `_manager` as the permission's manager
                                                  * @param _entity Address of the whitelisted entity that will be able to perform the role
                                                  * @param _app Address of the app in which the role will be allowed (requires app to depend on kernel for ACL)
                                                  * @param _role Identifier for the group of actions in app given access to perform
                                                  * @param _manager Address of the entity that will be able to grant and revoke the permission further.
                                                  */
                                                  function createPermission(address _entity, address _app, bytes32 _role, address _manager)
                                                      external
                                                      auth(CREATE_PERMISSIONS_ROLE)
                                                      noPermissionManager(_app, _role)
                                                  {
                                                      _createPermission(_entity, _app, _role, _manager);
                                                  }
                                              
                                                  /**
                                                  * @dev Grants permission if allowed. This requires `msg.sender` to be the permission manager
                                                  * @notice Grant `_entity` the ability to perform actions requiring `_role` on `_app`
                                                  * @param _entity Address of the whitelisted entity that will be able to perform the role
                                                  * @param _app Address of the app in which the role will be allowed (requires app to depend on kernel for ACL)
                                                  * @param _role Identifier for the group of actions in app given access to perform
                                                  */
                                                  function grantPermission(address _entity, address _app, bytes32 _role)
                                                      external
                                                  {
                                                      grantPermissionP(_entity, _app, _role, new uint256[](0));
                                                  }
                                              
                                                  /**
                                                  * @dev Grants a permission with parameters if allowed. This requires `msg.sender` to be the permission manager
                                                  * @notice Grant `_entity` the ability to perform actions requiring `_role` on `_app`
                                                  * @param _entity Address of the whitelisted entity that will be able to perform the role
                                                  * @param _app Address of the app in which the role will be allowed (requires app to depend on kernel for ACL)
                                                  * @param _role Identifier for the group of actions in app given access to perform
                                                  * @param _params Permission parameters
                                                  */
                                                  function grantPermissionP(address _entity, address _app, bytes32 _role, uint256[] _params)
                                                      public
                                                      onlyPermissionManager(_app, _role)
                                                  {
                                                      bytes32 paramsHash = _params.length > 0 ? _saveParams(_params) : EMPTY_PARAM_HASH;
                                                      _setPermission(_entity, _app, _role, paramsHash);
                                                  }
                                              
                                                  /**
                                                  * @dev Revokes permission if allowed. This requires `msg.sender` to be the the permission manager
                                                  * @notice Revoke from `_entity` the ability to perform actions requiring `_role` on `_app`
                                                  * @param _entity Address of the whitelisted entity to revoke access from
                                                  * @param _app Address of the app in which the role will be revoked
                                                  * @param _role Identifier for the group of actions in app being revoked
                                                  */
                                                  function revokePermission(address _entity, address _app, bytes32 _role)
                                                      external
                                                      onlyPermissionManager(_app, _role)
                                                  {
                                                      _setPermission(_entity, _app, _role, NO_PERMISSION);
                                                  }
                                              
                                                  /**
                                                  * @notice Set `_newManager` as the manager of `_role` in `_app`
                                                  * @param _newManager Address for the new manager
                                                  * @param _app Address of the app in which the permission management is being transferred
                                                  * @param _role Identifier for the group of actions being transferred
                                                  */
                                                  function setPermissionManager(address _newManager, address _app, bytes32 _role)
                                                      external
                                                      onlyPermissionManager(_app, _role)
                                                  {
                                                      _setPermissionManager(_newManager, _app, _role);
                                                  }
                                              
                                                  /**
                                                  * @notice Remove the manager of `_role` in `_app`
                                                  * @param _app Address of the app in which the permission is being unmanaged
                                                  * @param _role Identifier for the group of actions being unmanaged
                                                  */
                                                  function removePermissionManager(address _app, bytes32 _role)
                                                      external
                                                      onlyPermissionManager(_app, _role)
                                                  {
                                                      _setPermissionManager(address(0), _app, _role);
                                                  }
                                              
                                                  /**
                                                  * @notice Burn non-existent `_role` in `_app`, so no modification can be made to it (grant, revoke, permission manager)
                                                  * @param _app Address of the app in which the permission is being burned
                                                  * @param _role Identifier for the group of actions being burned
                                                  */
                                                  function createBurnedPermission(address _app, bytes32 _role)
                                                      external
                                                      auth(CREATE_PERMISSIONS_ROLE)
                                                      noPermissionManager(_app, _role)
                                                  {
                                                      _setPermissionManager(BURN_ENTITY, _app, _role);
                                                  }
                                              
                                                  /**
                                                  * @notice Burn `_role` in `_app`, so no modification can be made to it (grant, revoke, permission manager)
                                                  * @param _app Address of the app in which the permission is being burned
                                                  * @param _role Identifier for the group of actions being burned
                                                  */
                                                  function burnPermissionManager(address _app, bytes32 _role)
                                                      external
                                                      onlyPermissionManager(_app, _role)
                                                  {
                                                      _setPermissionManager(BURN_ENTITY, _app, _role);
                                                  }
                                              
                                                  /**
                                                   * @notice Get parameters for permission array length
                                                   * @param _entity Address of the whitelisted entity that will be able to perform the role
                                                   * @param _app Address of the app
                                                   * @param _role Identifier for a group of actions in app
                                                   * @return Length of the array
                                                   */
                                                  function getPermissionParamsLength(address _entity, address _app, bytes32 _role) external view returns (uint) {
                                                      return permissionParams[permissions[permissionHash(_entity, _app, _role)]].length;
                                                  }
                                              
                                                  /**
                                                  * @notice Get parameter for permission
                                                  * @param _entity Address of the whitelisted entity that will be able to perform the role
                                                  * @param _app Address of the app
                                                  * @param _role Identifier for a group of actions in app
                                                  * @param _index Index of parameter in the array
                                                  * @return Parameter (id, op, value)
                                                  */
                                                  function getPermissionParam(address _entity, address _app, bytes32 _role, uint _index)
                                                      external
                                                      view
                                                      returns (uint8, uint8, uint240)
                                                  {
                                                      Param storage param = permissionParams[permissions[permissionHash(_entity, _app, _role)]][_index];
                                                      return (param.id, param.op, param.value);
                                                  }
                                              
                                                  /**
                                                  * @dev Get manager for permission
                                                  * @param _app Address of the app
                                                  * @param _role Identifier for a group of actions in app
                                                  * @return address of the manager for the permission
                                                  */
                                                  function getPermissionManager(address _app, bytes32 _role) public view returns (address) {
                                                      return permissionManager[roleHash(_app, _role)];
                                                  }
                                              
                                                  /**
                                                  * @dev Function called by apps to check ACL on kernel or to check permission statu
                                                  * @param _who Sender of the original call
                                                  * @param _where Address of the app
                                                  * @param _where Identifier for a group of actions in app
                                                  * @param _how Permission parameters
                                                  * @return boolean indicating whether the ACL allows the role or not
                                                  */
                                                  function hasPermission(address _who, address _where, bytes32 _what, bytes memory _how) public view returns (bool) {
                                                      return hasPermission(_who, _where, _what, ConversionHelpers.dangerouslyCastBytesToUintArray(_how));
                                                  }
                                              
                                                  function hasPermission(address _who, address _where, bytes32 _what, uint256[] memory _how) public view returns (bool) {
                                                      bytes32 whoParams = permissions[permissionHash(_who, _where, _what)];
                                                      if (whoParams != NO_PERMISSION && evalParams(whoParams, _who, _where, _what, _how)) {
                                                          return true;
                                                      }
                                              
                                                      bytes32 anyParams = permissions[permissionHash(ANY_ENTITY, _where, _what)];
                                                      if (anyParams != NO_PERMISSION && evalParams(anyParams, ANY_ENTITY, _where, _what, _how)) {
                                                          return true;
                                                      }
                                              
                                                      return false;
                                                  }
                                              
                                                  function hasPermission(address _who, address _where, bytes32 _what) public view returns (bool) {
                                                      uint256[] memory empty = new uint256[](0);
                                                      return hasPermission(_who, _where, _what, empty);
                                                  }
                                              
                                                  function evalParams(
                                                      bytes32 _paramsHash,
                                                      address _who,
                                                      address _where,
                                                      bytes32 _what,
                                                      uint256[] _how
                                                  ) public view returns (bool)
                                                  {
                                                      if (_paramsHash == EMPTY_PARAM_HASH) {
                                                          return true;
                                                      }
                                              
                                                      return _evalParam(_paramsHash, 0, _who, _where, _what, _how);
                                                  }
                                              
                                                  /**
                                                  * @dev Internal createPermission for access inside the kernel (on instantiation)
                                                  */
                                                  function _createPermission(address _entity, address _app, bytes32 _role, address _manager) internal {
                                                      _setPermission(_entity, _app, _role, EMPTY_PARAM_HASH);
                                                      _setPermissionManager(_manager, _app, _role);
                                                  }
                                              
                                                  /**
                                                  * @dev Internal function called to actually save the permission
                                                  */
                                                  function _setPermission(address _entity, address _app, bytes32 _role, bytes32 _paramsHash) internal {
                                                      permissions[permissionHash(_entity, _app, _role)] = _paramsHash;
                                                      bool entityHasPermission = _paramsHash != NO_PERMISSION;
                                                      bool permissionHasParams = entityHasPermission && _paramsHash != EMPTY_PARAM_HASH;
                                              
                                                      emit SetPermission(_entity, _app, _role, entityHasPermission);
                                                      if (permissionHasParams) {
                                                          emit SetPermissionParams(_entity, _app, _role, _paramsHash);
                                                      }
                                                  }
                                              
                                                  function _saveParams(uint256[] _encodedParams) internal returns (bytes32) {
                                                      bytes32 paramHash = keccak256(abi.encodePacked(_encodedParams));
                                                      Param[] storage params = permissionParams[paramHash];
                                              
                                                      if (params.length == 0) { // params not saved before
                                                          for (uint256 i = 0; i < _encodedParams.length; i++) {
                                                              uint256 encodedParam = _encodedParams[i];
                                                              Param memory param = Param(decodeParamId(encodedParam), decodeParamOp(encodedParam), uint240(encodedParam));
                                                              params.push(param);
                                                          }
                                                      }
                                              
                                                      return paramHash;
                                                  }
                                              
                                                  function _evalParam(
                                                      bytes32 _paramsHash,
                                                      uint32 _paramId,
                                                      address _who,
                                                      address _where,
                                                      bytes32 _what,
                                                      uint256[] _how
                                                  ) internal view returns (bool)
                                                  {
                                                      if (_paramId >= permissionParams[_paramsHash].length) {
                                                          return false; // out of bounds
                                                      }
                                              
                                                      Param memory param = permissionParams[_paramsHash][_paramId];
                                              
                                                      if (param.id == LOGIC_OP_PARAM_ID) {
                                                          return _evalLogic(param, _paramsHash, _who, _where, _what, _how);
                                                      }
                                              
                                                      uint256 value;
                                                      uint256 comparedTo = uint256(param.value);
                                              
                                                      // get value
                                                      if (param.id == ORACLE_PARAM_ID) {
                                                          value = checkOracle(IACLOracle(param.value), _who, _where, _what, _how) ? 1 : 0;
                                                          comparedTo = 1;
                                                      } else if (param.id == BLOCK_NUMBER_PARAM_ID) {
                                                          value = getBlockNumber();
                                                      } else if (param.id == TIMESTAMP_PARAM_ID) {
                                                          value = getTimestamp();
                                                      } else if (param.id == PARAM_VALUE_PARAM_ID) {
                                                          value = uint256(param.value);
                                                      } else {
                                                          if (param.id >= _how.length) {
                                                              return false;
                                                          }
                                                          value = uint256(uint240(_how[param.id])); // force lost precision
                                                      }
                                              
                                                      if (Op(param.op) == Op.RET) {
                                                          return uint256(value) > 0;
                                                      }
                                              
                                                      return compare(value, Op(param.op), comparedTo);
                                                  }
                                              
                                                  function _evalLogic(Param _param, bytes32 _paramsHash, address _who, address _where, bytes32 _what, uint256[] _how)
                                                      internal
                                                      view
                                                      returns (bool)
                                                  {
                                                      if (Op(_param.op) == Op.IF_ELSE) {
                                                          uint32 conditionParam;
                                                          uint32 successParam;
                                                          uint32 failureParam;
                                              
                                                          (conditionParam, successParam, failureParam) = decodeParamsList(uint256(_param.value));
                                                          bool result = _evalParam(_paramsHash, conditionParam, _who, _where, _what, _how);
                                              
                                                          return _evalParam(_paramsHash, result ? successParam : failureParam, _who, _where, _what, _how);
                                                      }
                                              
                                                      uint32 param1;
                                                      uint32 param2;
                                              
                                                      (param1, param2,) = decodeParamsList(uint256(_param.value));
                                                      bool r1 = _evalParam(_paramsHash, param1, _who, _where, _what, _how);
                                              
                                                      if (Op(_param.op) == Op.NOT) {
                                                          return !r1;
                                                      }
                                              
                                                      if (r1 && Op(_param.op) == Op.OR) {
                                                          return true;
                                                      }
                                              
                                                      if (!r1 && Op(_param.op) == Op.AND) {
                                                          return false;
                                                      }
                                              
                                                      bool r2 = _evalParam(_paramsHash, param2, _who, _where, _what, _how);
                                              
                                                      if (Op(_param.op) == Op.XOR) {
                                                          return r1 != r2;
                                                      }
                                              
                                                      return r2; // both or and and depend on result of r2 after checks
                                                  }
                                              
                                                  function compare(uint256 _a, Op _op, uint256 _b) internal pure returns (bool) {
                                                      if (_op == Op.EQ)  return _a == _b;                              // solium-disable-line lbrace
                                                      if (_op == Op.NEQ) return _a != _b;                              // solium-disable-line lbrace
                                                      if (_op == Op.GT)  return _a > _b;                               // solium-disable-line lbrace
                                                      if (_op == Op.LT)  return _a < _b;                               // solium-disable-line lbrace
                                                      if (_op == Op.GTE) return _a >= _b;                              // solium-disable-line lbrace
                                                      if (_op == Op.LTE) return _a <= _b;                              // solium-disable-line lbrace
                                                      return false;
                                                  }
                                              
                                                  function checkOracle(IACLOracle _oracleAddr, address _who, address _where, bytes32 _what, uint256[] _how) internal view returns (bool) {
                                                      bytes4 sig = _oracleAddr.canPerform.selector;
                                              
                                                      // a raw call is required so we can return false if the call reverts, rather than reverting
                                                      bytes memory checkCalldata = abi.encodeWithSelector(sig, _who, _where, _what, _how);
                                              
                                                      bool ok;
                                                      assembly {
                                                          // send all available gas; if the oracle eats up all the gas, we will eventually revert
                                                          // note that we are currently guaranteed to still have some gas after the call from
                                                          // EIP-150's 63/64 gas forward rule
                                                          ok := staticcall(gas, _oracleAddr, add(checkCalldata, 0x20), mload(checkCalldata), 0, 0)
                                                      }
                                              
                                                      if (!ok) {
                                                          return false;
                                                      }
                                              
                                                      uint256 size;
                                                      assembly { size := returndatasize }
                                                      if (size != 32) {
                                                          return false;
                                                      }
                                              
                                                      bool result;
                                                      assembly {
                                                          let ptr := mload(0x40)       // get next free memory ptr
                                                          returndatacopy(ptr, 0, size) // copy return from above `staticcall`
                                                          result := mload(ptr)         // read data at ptr and set it to result
                                                          mstore(ptr, 0)               // set pointer memory to 0 so it still is the next free ptr
                                                      }
                                              
                                                      return result;
                                                  }
                                              
                                                  /**
                                                  * @dev Internal function that sets management
                                                  */
                                                  function _setPermissionManager(address _newManager, address _app, bytes32 _role) internal {
                                                      permissionManager[roleHash(_app, _role)] = _newManager;
                                                      emit ChangePermissionManager(_app, _role, _newManager);
                                                  }
                                              
                                                  function roleHash(address _where, bytes32 _what) internal pure returns (bytes32) {
                                                      return keccak256(abi.encodePacked("ROLE", _where, _what));
                                                  }
                                              
                                                  function permissionHash(address _who, address _where, bytes32 _what) internal pure returns (bytes32) {
                                                      return keccak256(abi.encodePacked("PERMISSION", _who, _where, _what));
                                                  }
                                              }
                                              
                                              // File: contracts/evmscript/ScriptHelpers.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              library ScriptHelpers {
                                                  function getSpecId(bytes _script) internal pure returns (uint32) {
                                                      return uint32At(_script, 0);
                                                  }
                                              
                                                  function uint256At(bytes _data, uint256 _location) internal pure returns (uint256 result) {
                                                      assembly {
                                                          result := mload(add(_data, add(0x20, _location)))
                                                      }
                                                  }
                                              
                                                  function addressAt(bytes _data, uint256 _location) internal pure returns (address result) {
                                                      uint256 word = uint256At(_data, _location);
                                              
                                                      assembly {
                                                          result := div(and(word, 0xffffffffffffffffffffffffffffffffffffffff000000000000000000000000),
                                                          0x1000000000000000000000000)
                                                      }
                                                  }
                                              
                                                  function uint32At(bytes _data, uint256 _location) internal pure returns (uint32 result) {
                                                      uint256 word = uint256At(_data, _location);
                                              
                                                      assembly {
                                                          result := div(and(word, 0xffffffff00000000000000000000000000000000000000000000000000000000),
                                                          0x100000000000000000000000000000000000000000000000000000000)
                                                      }
                                                  }
                                              
                                                  function locationOf(bytes _data, uint256 _location) internal pure returns (uint256 result) {
                                                      assembly {
                                                          result := add(_data, add(0x20, _location))
                                                      }
                                                  }
                                              
                                                  function toBytes(bytes4 _sig) internal pure returns (bytes) {
                                                      bytes memory payload = new bytes(4);
                                                      assembly { mstore(add(payload, 0x20), _sig) }
                                                      return payload;
                                                  }
                                              }
                                              
                                              // File: contracts/evmscript/EVMScriptRegistry.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              /* solium-disable function-order */
                                              // Allow public initialize() to be first
                                              contract EVMScriptRegistry is IEVMScriptRegistry, EVMScriptRegistryConstants, AragonApp {
                                                  using ScriptHelpers for bytes;
                                              
                                                  /* Hardcoded constants to save gas
                                                  bytes32 public constant REGISTRY_ADD_EXECUTOR_ROLE = keccak256("REGISTRY_ADD_EXECUTOR_ROLE");
                                                  bytes32 public constant REGISTRY_MANAGER_ROLE = keccak256("REGISTRY_MANAGER_ROLE");
                                                  */
                                                  bytes32 public constant REGISTRY_ADD_EXECUTOR_ROLE = 0xc4e90f38eea8c4212a009ca7b8947943ba4d4a58d19b683417f65291d1cd9ed2;
                                                  // WARN: Manager can censor all votes and the like happening in an org
                                                  bytes32 public constant REGISTRY_MANAGER_ROLE = 0xf7a450ef335e1892cb42c8ca72e7242359d7711924b75db5717410da3f614aa3;
                                              
                                                  uint256 internal constant SCRIPT_START_LOCATION = 4;
                                              
                                                  string private constant ERROR_INEXISTENT_EXECUTOR = "EVMREG_INEXISTENT_EXECUTOR";
                                                  string private constant ERROR_EXECUTOR_ENABLED = "EVMREG_EXECUTOR_ENABLED";
                                                  string private constant ERROR_EXECUTOR_DISABLED = "EVMREG_EXECUTOR_DISABLED";
                                                  string private constant ERROR_SCRIPT_LENGTH_TOO_SHORT = "EVMREG_SCRIPT_LENGTH_TOO_SHORT";
                                              
                                                  struct ExecutorEntry {
                                                      IEVMScriptExecutor executor;
                                                      bool enabled;
                                                  }
                                              
                                                  uint256 private executorsNextIndex;
                                                  mapping (uint256 => ExecutorEntry) public executors;
                                              
                                                  event EnableExecutor(uint256 indexed executorId, address indexed executorAddress);
                                                  event DisableExecutor(uint256 indexed executorId, address indexed executorAddress);
                                              
                                                  modifier executorExists(uint256 _executorId) {
                                                      require(_executorId > 0 && _executorId < executorsNextIndex, ERROR_INEXISTENT_EXECUTOR);
                                                      _;
                                                  }
                                              
                                                  /**
                                                  * @notice Initialize the registry
                                                  */
                                                  function initialize() public onlyInit {
                                                      initialized();
                                                      // Create empty record to begin executor IDs at 1
                                                      executorsNextIndex = 1;
                                                  }
                                              
                                                  /**
                                                  * @notice Add a new script executor with address `_executor` to the registry
                                                  * @param _executor Address of the IEVMScriptExecutor that will be added to the registry
                                                  * @return id Identifier of the executor in the registry
                                                  */
                                                  function addScriptExecutor(IEVMScriptExecutor _executor) external auth(REGISTRY_ADD_EXECUTOR_ROLE) returns (uint256 id) {
                                                      uint256 executorId = executorsNextIndex++;
                                                      executors[executorId] = ExecutorEntry(_executor, true);
                                                      emit EnableExecutor(executorId, _executor);
                                                      return executorId;
                                                  }
                                              
                                                  /**
                                                  * @notice Disable script executor with ID `_executorId`
                                                  * @param _executorId Identifier of the executor in the registry
                                                  */
                                                  function disableScriptExecutor(uint256 _executorId)
                                                      external
                                                      authP(REGISTRY_MANAGER_ROLE, arr(_executorId))
                                                  {
                                                      // Note that we don't need to check for an executor's existence in this case, as only
                                                      // existing executors can be enabled
                                                      ExecutorEntry storage executorEntry = executors[_executorId];
                                                      require(executorEntry.enabled, ERROR_EXECUTOR_DISABLED);
                                                      executorEntry.enabled = false;
                                                      emit DisableExecutor(_executorId, executorEntry.executor);
                                                  }
                                              
                                                  /**
                                                  * @notice Enable script executor with ID `_executorId`
                                                  * @param _executorId Identifier of the executor in the registry
                                                  */
                                                  function enableScriptExecutor(uint256 _executorId)
                                                      external
                                                      authP(REGISTRY_MANAGER_ROLE, arr(_executorId))
                                                      executorExists(_executorId)
                                                  {
                                                      ExecutorEntry storage executorEntry = executors[_executorId];
                                                      require(!executorEntry.enabled, ERROR_EXECUTOR_ENABLED);
                                                      executorEntry.enabled = true;
                                                      emit EnableExecutor(_executorId, executorEntry.executor);
                                                  }
                                              
                                                  /**
                                                  * @dev Get the script executor that can execute a particular script based on its first 4 bytes
                                                  * @param _script EVMScript being inspected
                                                  */
                                                  function getScriptExecutor(bytes _script) public view returns (IEVMScriptExecutor) {
                                                      require(_script.length >= SCRIPT_START_LOCATION, ERROR_SCRIPT_LENGTH_TOO_SHORT);
                                                      uint256 id = _script.getSpecId();
                                              
                                                      // Note that we don't need to check for an executor's existence in this case, as only
                                                      // existing executors can be enabled
                                                      ExecutorEntry storage entry = executors[id];
                                                      return entry.enabled ? entry.executor : IEVMScriptExecutor(0);
                                                  }
                                              }
                                              
                                              // File: contracts/evmscript/executors/BaseEVMScriptExecutor.sol
                                              
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              
                                              contract BaseEVMScriptExecutor is IEVMScriptExecutor, Autopetrified {
                                                  uint256 internal constant SCRIPT_START_LOCATION = 4;
                                              }
                                              
                                              // File: contracts/evmscript/executors/CallsScript.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              // Inspired by https://github.com/reverendus/tx-manager
                                              
                                              
                                              
                                              
                                              contract CallsScript is BaseEVMScriptExecutor {
                                                  using ScriptHelpers for bytes;
                                              
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant EXECUTOR_TYPE = keccak256("CALLS_SCRIPT");
                                                  */
                                                  bytes32 internal constant EXECUTOR_TYPE = 0x2dc858a00f3e417be1394b87c07158e989ec681ce8cc68a9093680ac1a870302;
                                              
                                                  string private constant ERROR_BLACKLISTED_CALL = "EVMCALLS_BLACKLISTED_CALL";
                                                  string private constant ERROR_INVALID_LENGTH = "EVMCALLS_INVALID_LENGTH";
                                              
                                                  /* This is manually crafted in assembly
                                                  string private constant ERROR_CALL_REVERTED = "EVMCALLS_CALL_REVERTED";
                                                  */
                                              
                                                  event LogScriptCall(address indexed sender, address indexed src, address indexed dst);
                                              
                                                  /**
                                                  * @notice Executes a number of call scripts
                                                  * @param _script [ specId (uint32) ] many calls with this structure ->
                                                  *    [ to (address: 20 bytes) ] [ calldataLength (uint32: 4 bytes) ] [ calldata (calldataLength bytes) ]
                                                  * @param _blacklist Addresses the script cannot call to, or will revert.
                                                  * @return Always returns empty byte array
                                                  */
                                                  function execScript(bytes _script, bytes, address[] _blacklist) external isInitialized returns (bytes) {
                                                      uint256 location = SCRIPT_START_LOCATION; // first 32 bits are spec id
                                                      while (location < _script.length) {
                                                          // Check there's at least address + calldataLength available
                                                          require(_script.length - location >= 0x18, ERROR_INVALID_LENGTH);
                                              
                                                          address contractAddress = _script.addressAt(location);
                                                          // Check address being called is not blacklist
                                                          for (uint256 i = 0; i < _blacklist.length; i++) {
                                                              require(contractAddress != _blacklist[i], ERROR_BLACKLISTED_CALL);
                                                          }
                                              
                                                          // logged before execution to ensure event ordering in receipt
                                                          // if failed entire execution is reverted regardless
                                                          emit LogScriptCall(msg.sender, address(this), contractAddress);
                                              
                                                          uint256 calldataLength = uint256(_script.uint32At(location + 0x14));
                                                          uint256 startOffset = location + 0x14 + 0x04;
                                                          uint256 calldataStart = _script.locationOf(startOffset);
                                              
                                                          // compute end of script / next location
                                                          location = startOffset + calldataLength;
                                                          require(location <= _script.length, ERROR_INVALID_LENGTH);
                                              
                                                          bool success;
                                                          assembly {
                                                              success := call(
                                                                  sub(gas, 5000),       // forward gas left - 5000
                                                                  contractAddress,      // address
                                                                  0,                    // no value
                                                                  calldataStart,        // calldata start
                                                                  calldataLength,       // calldata length
                                                                  0,                    // don't write output
                                                                  0                     // don't write output
                                                              )
                                              
                                                              switch success
                                                              case 0 {
                                                                  let ptr := mload(0x40)
                                              
                                                                  switch returndatasize
                                                                  case 0 {
                                                                      // No error data was returned, revert with "EVMCALLS_CALL_REVERTED"
                                                                      // See remix: doing a `revert("EVMCALLS_CALL_REVERTED")` always results in
                                                                      // this memory layout
                                                                      mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000)         // error identifier
                                                                      mstore(add(ptr, 0x04), 0x0000000000000000000000000000000000000000000000000000000000000020) // starting offset
                                                                      mstore(add(ptr, 0x24), 0x0000000000000000000000000000000000000000000000000000000000000016) // reason length
                                                                      mstore(add(ptr, 0x44), 0x45564d43414c4c535f43414c4c5f524556455254454400000000000000000000) // reason
                                              
                                                                      revert(ptr, 100) // 100 = 4 + 3 * 32 (error identifier + 3 words for the ABI encoded error)
                                                                  }
                                                                  default {
                                                                      // Forward the full error data
                                                                      returndatacopy(ptr, 0, returndatasize)
                                                                      revert(ptr, returndatasize)
                                                                  }
                                                              }
                                                              default { }
                                                          }
                                                      }
                                                      // No need to allocate empty bytes for the return as this can only be called via an delegatecall
                                                      // (due to the isInitialized modifier)
                                                  }
                                              
                                                  function executorType() external pure returns (bytes32) {
                                                      return EXECUTOR_TYPE;
                                                  }
                                              }
                                              
                                              // File: contracts/factory/EVMScriptRegistryFactory.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              contract EVMScriptRegistryFactory is EVMScriptRegistryConstants {
                                                  EVMScriptRegistry public baseReg;
                                                  IEVMScriptExecutor public baseCallScript;
                                              
                                                  /**
                                                  * @notice Create a new EVMScriptRegistryFactory.
                                                  */
                                                  constructor() public {
                                                      baseReg = new EVMScriptRegistry();
                                                      baseCallScript = IEVMScriptExecutor(new CallsScript());
                                                  }
                                              
                                                  /**
                                                  * @notice Install a new pinned instance of EVMScriptRegistry on `_dao`.
                                                  * @param _dao Kernel
                                                  * @return Installed EVMScriptRegistry
                                                  */
                                                  function newEVMScriptRegistry(Kernel _dao) public returns (EVMScriptRegistry reg) {
                                                      bytes memory initPayload = abi.encodeWithSelector(reg.initialize.selector);
                                                      reg = EVMScriptRegistry(_dao.newPinnedAppInstance(EVMSCRIPT_REGISTRY_APP_ID, baseReg, initPayload, true));
                                              
                                                      ACL acl = ACL(_dao.acl());
                                              
                                                      acl.createPermission(this, reg, reg.REGISTRY_ADD_EXECUTOR_ROLE(), this);
                                              
                                                      reg.addScriptExecutor(baseCallScript);     // spec 1 = CallsScript
                                              
                                                      // Clean up the permissions
                                                      acl.revokePermission(this, reg, reg.REGISTRY_ADD_EXECUTOR_ROLE());
                                                      acl.removePermissionManager(reg, reg.REGISTRY_ADD_EXECUTOR_ROLE());
                                              
                                                      return reg;
                                                  }
                                              }
                                              
                                              // File: contracts/factory/DAOFactory.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              contract DAOFactory {
                                                  IKernel public baseKernel;
                                                  IACL public baseACL;
                                                  EVMScriptRegistryFactory public regFactory;
                                              
                                                  event DeployDAO(address dao);
                                                  event DeployEVMScriptRegistry(address reg);
                                              
                                                  /**
                                                  * @notice Create a new DAOFactory, creating DAOs with Kernels proxied to `_baseKernel`, ACLs proxied to `_baseACL`, and new EVMScriptRegistries created from `_regFactory`.
                                                  * @param _baseKernel Base Kernel
                                                  * @param _baseACL Base ACL
                                                  * @param _regFactory EVMScriptRegistry factory
                                                  */
                                                  constructor(IKernel _baseKernel, IACL _baseACL, EVMScriptRegistryFactory _regFactory) public {
                                                      // No need to init as it cannot be killed by devops199
                                                      if (address(_regFactory) != address(0)) {
                                                          regFactory = _regFactory;
                                                      }
                                              
                                                      baseKernel = _baseKernel;
                                                      baseACL = _baseACL;
                                                  }
                                              
                                                  /**
                                                  * @notice Create a new DAO with `_root` set as the initial admin
                                                  * @param _root Address that will be granted control to setup DAO permissions
                                                  * @return Newly created DAO
                                                  */
                                                  function newDAO(address _root) public returns (Kernel) {
                                                      Kernel dao = Kernel(new KernelProxy(baseKernel));
                                              
                                                      if (address(regFactory) == address(0)) {
                                                          dao.initialize(baseACL, _root);
                                                      } else {
                                                          dao.initialize(baseACL, this);
                                              
                                                          ACL acl = ACL(dao.acl());
                                                          bytes32 permRole = acl.CREATE_PERMISSIONS_ROLE();
                                                          bytes32 appManagerRole = dao.APP_MANAGER_ROLE();
                                              
                                                          acl.grantPermission(regFactory, acl, permRole);
                                              
                                                          acl.createPermission(regFactory, dao, appManagerRole, this);
                                              
                                                          EVMScriptRegistry reg = regFactory.newEVMScriptRegistry(dao);
                                                          emit DeployEVMScriptRegistry(address(reg));
                                              
                                                          // Clean up permissions
                                                          // First, completely reset the APP_MANAGER_ROLE
                                                          acl.revokePermission(regFactory, dao, appManagerRole);
                                                          acl.removePermissionManager(dao, appManagerRole);
                                              
                                                          // Then, make root the only holder and manager of CREATE_PERMISSIONS_ROLE
                                                          acl.revokePermission(regFactory, acl, permRole);
                                                          acl.revokePermission(this, acl, permRole);
                                                          acl.grantPermission(_root, acl, permRole);
                                                          acl.setPermissionManager(_root, acl, permRole);
                                                      }
                                              
                                                      emit DeployDAO(address(dao));
                                              
                                                      return dao;
                                                  }
                                              }

                                              File 5 of 6: Kernel
                                              // File: contracts/acl/IACL.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              interface IACL {
                                                  function initialize(address permissionsCreator) external;
                                              
                                                  // TODO: this should be external
                                                  // See https://github.com/ethereum/solidity/issues/4832
                                                  function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);
                                              }
                                              
                                              // File: contracts/common/IVaultRecoverable.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              interface IVaultRecoverable {
                                                  event RecoverToVault(address indexed vault, address indexed token, uint256 amount);
                                              
                                                  function transferToVault(address token) external;
                                              
                                                  function allowRecoverability(address token) external view returns (bool);
                                                  function getRecoveryVault() external view returns (address);
                                              }
                                              
                                              // File: contracts/kernel/IKernel.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              
                                              interface IKernelEvents {
                                                  event SetApp(bytes32 indexed namespace, bytes32 indexed appId, address app);
                                              }
                                              
                                              
                                              // This should be an interface, but interfaces can't inherit yet :(
                                              contract IKernel is IKernelEvents, IVaultRecoverable {
                                                  function acl() public view returns (IACL);
                                                  function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);
                                              
                                                  function setApp(bytes32 namespace, bytes32 appId, address app) public;
                                                  function getApp(bytes32 namespace, bytes32 appId) public view returns (address);
                                              }
                                              
                                              // File: contracts/kernel/KernelConstants.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              contract KernelAppIds {
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant KERNEL_CORE_APP_ID = apmNamehash("kernel");
                                                  bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = apmNamehash("acl");
                                                  bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = apmNamehash("vault");
                                                  */
                                                  bytes32 internal constant KERNEL_CORE_APP_ID = 0x3b4bf6bf3ad5000ecf0f989d5befde585c6860fea3e574a4fab4c49d1c177d9c;
                                                  bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = 0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a;
                                                  bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = 0x7e852e0fcfce6551c13800f1e7476f982525c2b5277ba14b24339c68416336d1;
                                              }
                                              
                                              
                                              contract KernelNamespaceConstants {
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant KERNEL_CORE_NAMESPACE = keccak256("core");
                                                  bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = keccak256("base");
                                                  bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = keccak256("app");
                                                  */
                                                  bytes32 internal constant KERNEL_CORE_NAMESPACE = 0xc681a85306374a5ab27f0bbc385296a54bcd314a1948b6cf61c4ea1bc44bb9f8;
                                                  bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = 0xf1f3eb40f5bc1ad1344716ced8b8a0431d840b5783aea1fd01786bc26f35ac0f;
                                                  bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
                                              }
                                              
                                              // File: contracts/kernel/KernelStorage.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              contract KernelStorage {
                                                  // namespace => app id => address
                                                  mapping (bytes32 => mapping (bytes32 => address)) public apps;
                                                  bytes32 public recoveryVaultAppId;
                                              }
                                              
                                              // File: contracts/acl/ACLSyntaxSugar.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              contract ACLSyntaxSugar {
                                                  function arr() internal pure returns (uint256[]) {
                                                      return new uint256[](0);
                                                  }
                                              
                                                  function arr(bytes32 _a) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a));
                                                  }
                                              
                                                  function arr(bytes32 _a, bytes32 _b) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b));
                                                  }
                                              
                                                  function arr(address _a) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a));
                                                  }
                                              
                                                  function arr(address _a, address _b) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b));
                                                  }
                                              
                                                  function arr(address _a, uint256 _b, uint256 _c) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), _b, _c);
                                                  }
                                              
                                                  function arr(address _a, uint256 _b, uint256 _c, uint256 _d) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), _b, _c, _d);
                                                  }
                                              
                                                  function arr(address _a, uint256 _b) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b));
                                                  }
                                              
                                                  function arr(address _a, address _b, uint256 _c, uint256 _d, uint256 _e) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b), _c, _d, _e);
                                                  }
                                              
                                                  function arr(address _a, address _b, address _c) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b), uint256(_c));
                                                  }
                                              
                                                  function arr(address _a, address _b, uint256 _c) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b), uint256(_c));
                                                  }
                                              
                                                  function arr(uint256 _a) internal pure returns (uint256[] r) {
                                                      r = new uint256[](1);
                                                      r[0] = _a;
                                                  }
                                              
                                                  function arr(uint256 _a, uint256 _b) internal pure returns (uint256[] r) {
                                                      r = new uint256[](2);
                                                      r[0] = _a;
                                                      r[1] = _b;
                                                  }
                                              
                                                  function arr(uint256 _a, uint256 _b, uint256 _c) internal pure returns (uint256[] r) {
                                                      r = new uint256[](3);
                                                      r[0] = _a;
                                                      r[1] = _b;
                                                      r[2] = _c;
                                                  }
                                              
                                                  function arr(uint256 _a, uint256 _b, uint256 _c, uint256 _d) internal pure returns (uint256[] r) {
                                                      r = new uint256[](4);
                                                      r[0] = _a;
                                                      r[1] = _b;
                                                      r[2] = _c;
                                                      r[3] = _d;
                                                  }
                                              
                                                  function arr(uint256 _a, uint256 _b, uint256 _c, uint256 _d, uint256 _e) internal pure returns (uint256[] r) {
                                                      r = new uint256[](5);
                                                      r[0] = _a;
                                                      r[1] = _b;
                                                      r[2] = _c;
                                                      r[3] = _d;
                                                      r[4] = _e;
                                                  }
                                              }
                                              
                                              
                                              contract ACLHelpers {
                                                  function decodeParamOp(uint256 _x) internal pure returns (uint8 b) {
                                                      return uint8(_x >> (8 * 30));
                                                  }
                                              
                                                  function decodeParamId(uint256 _x) internal pure returns (uint8 b) {
                                                      return uint8(_x >> (8 * 31));
                                                  }
                                              
                                                  function decodeParamsList(uint256 _x) internal pure returns (uint32 a, uint32 b, uint32 c) {
                                                      a = uint32(_x);
                                                      b = uint32(_x >> (8 * 4));
                                                      c = uint32(_x >> (8 * 8));
                                                  }
                                              }
                                              
                                              // File: contracts/common/ConversionHelpers.sol
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              library ConversionHelpers {
                                                  string private constant ERROR_IMPROPER_LENGTH = "CONVERSION_IMPROPER_LENGTH";
                                              
                                                  function dangerouslyCastUintArrayToBytes(uint256[] memory _input) internal pure returns (bytes memory output) {
                                                      // Force cast the uint256[] into a bytes array, by overwriting its length
                                                      // Note that the bytes array doesn't need to be initialized as we immediately overwrite it
                                                      // with the input and a new length. The input becomes invalid from this point forward.
                                                      uint256 byteLength = _input.length * 32;
                                                      assembly {
                                                          output := _input
                                                          mstore(output, byteLength)
                                                      }
                                                  }
                                              
                                                  function dangerouslyCastBytesToUintArray(bytes memory _input) internal pure returns (uint256[] memory output) {
                                                      // Force cast the bytes array into a uint256[], by overwriting its length
                                                      // Note that the uint256[] doesn't need to be initialized as we immediately overwrite it
                                                      // with the input and a new length. The input becomes invalid from this point forward.
                                                      uint256 intsLength = _input.length / 32;
                                                      require(_input.length == intsLength * 32, ERROR_IMPROPER_LENGTH);
                                              
                                                      assembly {
                                                          output := _input
                                                          mstore(output, intsLength)
                                                      }
                                                  }
                                              }
                                              
                                              // File: contracts/common/IsContract.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              contract IsContract {
                                                  /*
                                                  * NOTE: this should NEVER be used for authentication
                                                  * (see pitfalls: https://github.com/fergarrui/ethereum-security/tree/master/contracts/extcodesize).
                                                  *
                                                  * This is only intended to be used as a sanity check that an address is actually a contract,
                                                  * RATHER THAN an address not being a contract.
                                                  */
                                                  function isContract(address _target) internal view returns (bool) {
                                                      if (_target == address(0)) {
                                                          return false;
                                                      }
                                              
                                                      uint256 size;
                                                      assembly { size := extcodesize(_target) }
                                                      return size > 0;
                                                  }
                                              }
                                              
                                              // File: contracts/common/Uint256Helpers.sol
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              library Uint256Helpers {
                                                  uint256 private constant MAX_UINT64 = uint64(-1);
                                              
                                                  string private constant ERROR_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG";
                                              
                                                  function toUint64(uint256 a) internal pure returns (uint64) {
                                                      require(a <= MAX_UINT64, ERROR_NUMBER_TOO_BIG);
                                                      return uint64(a);
                                                  }
                                              }
                                              
                                              // File: contracts/common/TimeHelpers.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              contract TimeHelpers {
                                                  using Uint256Helpers for uint256;
                                              
                                                  /**
                                                  * @dev Returns the current block number.
                                                  *      Using a function rather than `block.number` allows us to easily mock the block number in
                                                  *      tests.
                                                  */
                                                  function getBlockNumber() internal view returns (uint256) {
                                                      return block.number;
                                                  }
                                              
                                                  /**
                                                  * @dev Returns the current block number, converted to uint64.
                                                  *      Using a function rather than `block.number` allows us to easily mock the block number in
                                                  *      tests.
                                                  */
                                                  function getBlockNumber64() internal view returns (uint64) {
                                                      return getBlockNumber().toUint64();
                                                  }
                                              
                                                  /**
                                                  * @dev Returns the current timestamp.
                                                  *      Using a function rather than `block.timestamp` allows us to easily mock it in
                                                  *      tests.
                                                  */
                                                  function getTimestamp() internal view returns (uint256) {
                                                      return block.timestamp; // solium-disable-line security/no-block-members
                                                  }
                                              
                                                  /**
                                                  * @dev Returns the current timestamp, converted to uint64.
                                                  *      Using a function rather than `block.timestamp` allows us to easily mock it in
                                                  *      tests.
                                                  */
                                                  function getTimestamp64() internal view returns (uint64) {
                                                      return getTimestamp().toUint64();
                                                  }
                                              }
                                              
                                              // File: contracts/common/UnstructuredStorage.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              library UnstructuredStorage {
                                                  function getStorageBool(bytes32 position) internal view returns (bool data) {
                                                      assembly { data := sload(position) }
                                                  }
                                              
                                                  function getStorageAddress(bytes32 position) internal view returns (address data) {
                                                      assembly { data := sload(position) }
                                                  }
                                              
                                                  function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) {
                                                      assembly { data := sload(position) }
                                                  }
                                              
                                                  function getStorageUint256(bytes32 position) internal view returns (uint256 data) {
                                                      assembly { data := sload(position) }
                                                  }
                                              
                                                  function setStorageBool(bytes32 position, bool data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                              
                                                  function setStorageAddress(bytes32 position, address data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                              
                                                  function setStorageBytes32(bytes32 position, bytes32 data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                              
                                                  function setStorageUint256(bytes32 position, uint256 data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                              }
                                              
                                              // File: contracts/common/Initializable.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              
                                              contract Initializable is TimeHelpers {
                                                  using UnstructuredStorage for bytes32;
                                              
                                                  // keccak256("aragonOS.initializable.initializationBlock")
                                                  bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e;
                                              
                                                  string private constant ERROR_ALREADY_INITIALIZED = "INIT_ALREADY_INITIALIZED";
                                                  string private constant ERROR_NOT_INITIALIZED = "INIT_NOT_INITIALIZED";
                                              
                                                  modifier onlyInit {
                                                      require(getInitializationBlock() == 0, ERROR_ALREADY_INITIALIZED);
                                                      _;
                                                  }
                                              
                                                  modifier isInitialized {
                                                      require(hasInitialized(), ERROR_NOT_INITIALIZED);
                                                      _;
                                                  }
                                              
                                                  /**
                                                  * @return Block number in which the contract was initialized
                                                  */
                                                  function getInitializationBlock() public view returns (uint256) {
                                                      return INITIALIZATION_BLOCK_POSITION.getStorageUint256();
                                                  }
                                              
                                                  /**
                                                  * @return Whether the contract has been initialized by the time of the current block
                                                  */
                                                  function hasInitialized() public view returns (bool) {
                                                      uint256 initializationBlock = getInitializationBlock();
                                                      return initializationBlock != 0 && getBlockNumber() >= initializationBlock;
                                                  }
                                              
                                                  /**
                                                  * @dev Function to be called by top level contract after initialization has finished.
                                                  */
                                                  function initialized() internal onlyInit {
                                                      INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber());
                                                  }
                                              
                                                  /**
                                                  * @dev Function to be called by top level contract after initialization to enable the contract
                                                  *      at a future block number rather than immediately.
                                                  */
                                                  function initializedAt(uint256 _blockNumber) internal onlyInit {
                                                      INITIALIZATION_BLOCK_POSITION.setStorageUint256(_blockNumber);
                                                  }
                                              }
                                              
                                              // File: contracts/common/Petrifiable.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              contract Petrifiable is Initializable {
                                                  // Use block UINT256_MAX (which should be never) as the initializable date
                                                  uint256 internal constant PETRIFIED_BLOCK = uint256(-1);
                                              
                                                  function isPetrified() public view returns (bool) {
                                                      return getInitializationBlock() == PETRIFIED_BLOCK;
                                                  }
                                              
                                                  /**
                                                  * @dev Function to be called by top level contract to prevent being initialized.
                                                  *      Useful for freezing base contracts when they're used behind proxies.
                                                  */
                                                  function petrify() internal onlyInit {
                                                      initializedAt(PETRIFIED_BLOCK);
                                                  }
                                              }
                                              
                                              // File: contracts/lib/token/ERC20.sol
                                              
                                              // See https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a9f910d34f0ab33a1ae5e714f69f9596a02b4d91/contracts/token/ERC20/ERC20.sol
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              /**
                                               * @title ERC20 interface
                                               * @dev see https://github.com/ethereum/EIPs/issues/20
                                               */
                                              contract ERC20 {
                                                  function totalSupply() public view returns (uint256);
                                              
                                                  function balanceOf(address _who) public view returns (uint256);
                                              
                                                  function allowance(address _owner, address _spender)
                                                      public view returns (uint256);
                                              
                                                  function transfer(address _to, uint256 _value) public returns (bool);
                                              
                                                  function approve(address _spender, uint256 _value)
                                                      public returns (bool);
                                              
                                                  function transferFrom(address _from, address _to, uint256 _value)
                                                      public returns (bool);
                                              
                                                  event Transfer(
                                                      address indexed from,
                                                      address indexed to,
                                                      uint256 value
                                                  );
                                              
                                                  event Approval(
                                                      address indexed owner,
                                                      address indexed spender,
                                                      uint256 value
                                                  );
                                              }
                                              
                                              // File: contracts/common/EtherTokenConstant.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              // aragonOS and aragon-apps rely on address(0) to denote native ETH, in
                                              // contracts where both tokens and ETH are accepted
                                              contract EtherTokenConstant {
                                                  address internal constant ETH = address(0);
                                              }
                                              
                                              // File: contracts/common/SafeERC20.sol
                                              
                                              // Inspired by AdEx (https://github.com/AdExNetwork/adex-protocol-eth/blob/b9df617829661a7518ee10f4cb6c4108659dd6d5/contracts/libs/SafeERC20.sol)
                                              // and 0x (https://github.com/0xProject/0x-monorepo/blob/737d1dc54d72872e24abce5a1dbe1b66d35fa21a/contracts/protocol/contracts/protocol/AssetProxy/ERC20Proxy.sol#L143)
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              library SafeERC20 {
                                                  // Before 0.5, solidity has a mismatch between `address.transfer()` and `token.transfer()`:
                                                  // https://github.com/ethereum/solidity/issues/3544
                                                  bytes4 private constant TRANSFER_SELECTOR = 0xa9059cbb;
                                              
                                                  string private constant ERROR_TOKEN_BALANCE_REVERTED = "SAFE_ERC_20_BALANCE_REVERTED";
                                                  string private constant ERROR_TOKEN_ALLOWANCE_REVERTED = "SAFE_ERC_20_ALLOWANCE_REVERTED";
                                              
                                                  function invokeAndCheckSuccess(address _addr, bytes memory _calldata)
                                                      private
                                                      returns (bool)
                                                  {
                                                      bool ret;
                                                      assembly {
                                                          let ptr := mload(0x40)    // free memory pointer
                                              
                                                          let success := call(
                                                              gas,                  // forward all gas
                                                              _addr,                // address
                                                              0,                    // no value
                                                              add(_calldata, 0x20), // calldata start
                                                              mload(_calldata),     // calldata length
                                                              ptr,                  // write output over free memory
                                                              0x20                  // uint256 return
                                                          )
                                              
                                                          if gt(success, 0) {
                                                              // Check number of bytes returned from last function call
                                                              switch returndatasize
                                              
                                                              // No bytes returned: assume success
                                                              case 0 {
                                                                  ret := 1
                                                              }
                                              
                                                              // 32 bytes returned: check if non-zero
                                                              case 0x20 {
                                                                  // Only return success if returned data was true
                                                                  // Already have output in ptr
                                                                  ret := eq(mload(ptr), 1)
                                                              }
                                              
                                                              // Not sure what was returned: don't mark as success
                                                              default { }
                                                          }
                                                      }
                                                      return ret;
                                                  }
                                              
                                                  function staticInvoke(address _addr, bytes memory _calldata)
                                                      private
                                                      view
                                                      returns (bool, uint256)
                                                  {
                                                      bool success;
                                                      uint256 ret;
                                                      assembly {
                                                          let ptr := mload(0x40)    // free memory pointer
                                              
                                                          success := staticcall(
                                                              gas,                  // forward all gas
                                                              _addr,                // address
                                                              add(_calldata, 0x20), // calldata start
                                                              mload(_calldata),     // calldata length
                                                              ptr,                  // write output over free memory
                                                              0x20                  // uint256 return
                                                          )
                                              
                                                          if gt(success, 0) {
                                                              ret := mload(ptr)
                                                          }
                                                      }
                                                      return (success, ret);
                                                  }
                                              
                                                  /**
                                                  * @dev Same as a standards-compliant ERC20.transfer() that never reverts (returns false).
                                                  *      Note that this makes an external call to the token.
                                                  */
                                                  function safeTransfer(ERC20 _token, address _to, uint256 _amount) internal returns (bool) {
                                                      bytes memory transferCallData = abi.encodeWithSelector(
                                                          TRANSFER_SELECTOR,
                                                          _to,
                                                          _amount
                                                      );
                                                      return invokeAndCheckSuccess(_token, transferCallData);
                                                  }
                                              
                                                  /**
                                                  * @dev Same as a standards-compliant ERC20.transferFrom() that never reverts (returns false).
                                                  *      Note that this makes an external call to the token.
                                                  */
                                                  function safeTransferFrom(ERC20 _token, address _from, address _to, uint256 _amount) internal returns (bool) {
                                                      bytes memory transferFromCallData = abi.encodeWithSelector(
                                                          _token.transferFrom.selector,
                                                          _from,
                                                          _to,
                                                          _amount
                                                      );
                                                      return invokeAndCheckSuccess(_token, transferFromCallData);
                                                  }
                                              
                                                  /**
                                                  * @dev Same as a standards-compliant ERC20.approve() that never reverts (returns false).
                                                  *      Note that this makes an external call to the token.
                                                  */
                                                  function safeApprove(ERC20 _token, address _spender, uint256 _amount) internal returns (bool) {
                                                      bytes memory approveCallData = abi.encodeWithSelector(
                                                          _token.approve.selector,
                                                          _spender,
                                                          _amount
                                                      );
                                                      return invokeAndCheckSuccess(_token, approveCallData);
                                                  }
                                              
                                                  /**
                                                  * @dev Static call into ERC20.balanceOf().
                                                  * Reverts if the call fails for some reason (should never fail).
                                                  */
                                                  function staticBalanceOf(ERC20 _token, address _owner) internal view returns (uint256) {
                                                      bytes memory balanceOfCallData = abi.encodeWithSelector(
                                                          _token.balanceOf.selector,
                                                          _owner
                                                      );
                                              
                                                      (bool success, uint256 tokenBalance) = staticInvoke(_token, balanceOfCallData);
                                                      require(success, ERROR_TOKEN_BALANCE_REVERTED);
                                              
                                                      return tokenBalance;
                                                  }
                                              
                                                  /**
                                                  * @dev Static call into ERC20.allowance().
                                                  * Reverts if the call fails for some reason (should never fail).
                                                  */
                                                  function staticAllowance(ERC20 _token, address _owner, address _spender) internal view returns (uint256) {
                                                      bytes memory allowanceCallData = abi.encodeWithSelector(
                                                          _token.allowance.selector,
                                                          _owner,
                                                          _spender
                                                      );
                                              
                                                      (bool success, uint256 allowance) = staticInvoke(_token, allowanceCallData);
                                                      require(success, ERROR_TOKEN_ALLOWANCE_REVERTED);
                                              
                                                      return allowance;
                                                  }
                                              
                                                  /**
                                                  * @dev Static call into ERC20.totalSupply().
                                                  * Reverts if the call fails for some reason (should never fail).
                                                  */
                                                  function staticTotalSupply(ERC20 _token) internal view returns (uint256) {
                                                      bytes memory totalSupplyCallData = abi.encodeWithSelector(_token.totalSupply.selector);
                                              
                                                      (bool success, uint256 totalSupply) = staticInvoke(_token, totalSupplyCallData);
                                                      require(success, ERROR_TOKEN_ALLOWANCE_REVERTED);
                                              
                                                      return totalSupply;
                                                  }
                                              }
                                              
                                              // File: contracts/common/VaultRecoverable.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              contract VaultRecoverable is IVaultRecoverable, EtherTokenConstant, IsContract {
                                                  using SafeERC20 for ERC20;
                                              
                                                  string private constant ERROR_DISALLOWED = "RECOVER_DISALLOWED";
                                                  string private constant ERROR_VAULT_NOT_CONTRACT = "RECOVER_VAULT_NOT_CONTRACT";
                                                  string private constant ERROR_TOKEN_TRANSFER_FAILED = "RECOVER_TOKEN_TRANSFER_FAILED";
                                              
                                                  /**
                                                   * @notice Send funds to recovery Vault. This contract should never receive funds,
                                                   *         but in case it does, this function allows one to recover them.
                                                   * @param _token Token balance to be sent to recovery vault.
                                                   */
                                                  function transferToVault(address _token) external {
                                                      require(allowRecoverability(_token), ERROR_DISALLOWED);
                                                      address vault = getRecoveryVault();
                                                      require(isContract(vault), ERROR_VAULT_NOT_CONTRACT);
                                              
                                                      uint256 balance;
                                                      if (_token == ETH) {
                                                          balance = address(this).balance;
                                                          vault.transfer(balance);
                                                      } else {
                                                          ERC20 token = ERC20(_token);
                                                          balance = token.staticBalanceOf(this);
                                                          require(token.safeTransfer(vault, balance), ERROR_TOKEN_TRANSFER_FAILED);
                                                      }
                                              
                                                      emit RecoverToVault(vault, _token, balance);
                                                  }
                                              
                                                  /**
                                                  * @dev By default deriving from AragonApp makes it recoverable
                                                  * @param token Token address that would be recovered
                                                  * @return bool whether the app allows the recovery
                                                  */
                                                  function allowRecoverability(address token) public view returns (bool) {
                                                      return true;
                                                  }
                                              
                                                  // Cast non-implemented interface to be public so we can use it internally
                                                  function getRecoveryVault() public view returns (address);
                                              }
                                              
                                              // File: contracts/apps/AppStorage.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              
                                              
                                              contract AppStorage {
                                                  using UnstructuredStorage for bytes32;
                                              
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant KERNEL_POSITION = keccak256("aragonOS.appStorage.kernel");
                                                  bytes32 internal constant APP_ID_POSITION = keccak256("aragonOS.appStorage.appId");
                                                  */
                                                  bytes32 internal constant KERNEL_POSITION = 0x4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b;
                                                  bytes32 internal constant APP_ID_POSITION = 0xd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b;
                                              
                                                  function kernel() public view returns (IKernel) {
                                                      return IKernel(KERNEL_POSITION.getStorageAddress());
                                                  }
                                              
                                                  function appId() public view returns (bytes32) {
                                                      return APP_ID_POSITION.getStorageBytes32();
                                                  }
                                              
                                                  function setKernel(IKernel _kernel) internal {
                                                      KERNEL_POSITION.setStorageAddress(address(_kernel));
                                                  }
                                              
                                                  function setAppId(bytes32 _appId) internal {
                                                      APP_ID_POSITION.setStorageBytes32(_appId);
                                                  }
                                              }
                                              
                                              // File: contracts/lib/misc/ERCProxy.sol
                                              
                                              /*
                                               * SPDX-License-Identitifer:    MIT
                                               */
                                              
                                              pragma solidity ^0.4.24;
                                              
                                              
                                              contract ERCProxy {
                                                  uint256 internal constant FORWARDING = 1;
                                                  uint256 internal constant UPGRADEABLE = 2;
                                              
                                                  function proxyType() public pure returns (uint256 proxyTypeId);
                                                  function implementation() public view returns (address codeAddr);
                                              }
                                              
                                              // File: contracts/common/DelegateProxy.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              contract DelegateProxy is ERCProxy, IsContract {
                                                  uint256 internal constant FWD_GAS_LIMIT = 10000;
                                              
                                                  /**
                                                  * @dev Performs a delegatecall and returns whatever the delegatecall returned (entire context execution will return!)
                                                  * @param _dst Destination address to perform the delegatecall
                                                  * @param _calldata Calldata for the delegatecall
                                                  */
                                                  function delegatedFwd(address _dst, bytes _calldata) internal {
                                                      require(isContract(_dst));
                                                      uint256 fwdGasLimit = FWD_GAS_LIMIT;
                                              
                                                      assembly {
                                                          let result := delegatecall(sub(gas, fwdGasLimit), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0)
                                                          let size := returndatasize
                                                          let ptr := mload(0x40)
                                                          returndatacopy(ptr, 0, size)
                                              
                                                          // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
                                                          // if the call returned error data, forward it
                                                          switch result case 0 { revert(ptr, size) }
                                                          default { return(ptr, size) }
                                                      }
                                                  }
                                              }
                                              
                                              // File: contracts/common/DepositableStorage.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              contract DepositableStorage {
                                                  using UnstructuredStorage for bytes32;
                                              
                                                  // keccak256("aragonOS.depositableStorage.depositable")
                                                  bytes32 internal constant DEPOSITABLE_POSITION = 0x665fd576fbbe6f247aff98f5c94a561e3f71ec2d3c988d56f12d342396c50cea;
                                              
                                                  function isDepositable() public view returns (bool) {
                                                      return DEPOSITABLE_POSITION.getStorageBool();
                                                  }
                                              
                                                  function setDepositable(bool _depositable) internal {
                                                      DEPOSITABLE_POSITION.setStorageBool(_depositable);
                                                  }
                                              }
                                              
                                              // File: contracts/common/DepositableDelegateProxy.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              contract DepositableDelegateProxy is DepositableStorage, DelegateProxy {
                                                  event ProxyDeposit(address sender, uint256 value);
                                              
                                                  function () external payable {
                                                      uint256 forwardGasThreshold = FWD_GAS_LIMIT;
                                                      bytes32 isDepositablePosition = DEPOSITABLE_POSITION;
                                              
                                                      // Optimized assembly implementation to prevent EIP-1884 from breaking deposits, reference code in Solidity:
                                                      // https://github.com/aragon/aragonOS/blob/v4.2.1/contracts/common/DepositableDelegateProxy.sol#L10-L20
                                                      assembly {
                                                          // Continue only if the gas left is lower than the threshold for forwarding to the implementation code,
                                                          // otherwise continue outside of the assembly block.
                                                          if lt(gas, forwardGasThreshold) {
                                                              // Only accept the deposit and emit an event if all of the following are true:
                                                              // the proxy accepts deposits (isDepositable), msg.data.length == 0, and msg.value > 0
                                                              if and(and(sload(isDepositablePosition), iszero(calldatasize)), gt(callvalue, 0)) {
                                                                  // Equivalent Solidity code for emitting the event:
                                                                  // emit ProxyDeposit(msg.sender, msg.value);
                                              
                                                                  let logData := mload(0x40) // free memory pointer
                                                                  mstore(logData, caller) // add 'msg.sender' to the log data (first event param)
                                                                  mstore(add(logData, 0x20), callvalue) // add 'msg.value' to the log data (second event param)
                                              
                                                                  // Emit an event with one topic to identify the event: keccak256('ProxyDeposit(address,uint256)') = 0x15ee...dee1
                                                                  log1(logData, 0x40, 0x15eeaa57c7bd188c1388020bcadc2c436ec60d647d36ef5b9eb3c742217ddee1)
                                              
                                                                  stop() // Stop. Exits execution context
                                                              }
                                              
                                                              // If any of above checks failed, revert the execution (if ETH was sent, it is returned to the sender)
                                                              revert(0, 0)
                                                          }
                                                      }
                                              
                                                      address target = implementation();
                                                      delegatedFwd(target, msg.data);
                                                  }
                                              }
                                              
                                              // File: contracts/apps/AppProxyBase.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              contract AppProxyBase is AppStorage, DepositableDelegateProxy, KernelNamespaceConstants {
                                                  /**
                                                  * @dev Initialize AppProxy
                                                  * @param _kernel Reference to organization kernel for the app
                                                  * @param _appId Identifier for app
                                                  * @param _initializePayload Payload for call to be made after setup to initialize
                                                  */
                                                  constructor(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public {
                                                      setKernel(_kernel);
                                                      setAppId(_appId);
                                              
                                                      // Implicit check that kernel is actually a Kernel
                                                      // The EVM doesn't actually provide a way for us to make sure, but we can force a revert to
                                                      // occur if the kernel is set to 0x0 or a non-code address when we try to call a method on
                                                      // it.
                                                      address appCode = getAppBase(_appId);
                                              
                                                      // If initialize payload is provided, it will be executed
                                                      if (_initializePayload.length > 0) {
                                                          require(isContract(appCode));
                                                          // Cannot make delegatecall as a delegateproxy.delegatedFwd as it
                                                          // returns ending execution context and halts contract deployment
                                                          require(appCode.delegatecall(_initializePayload));
                                                      }
                                                  }
                                              
                                                  function getAppBase(bytes32 _appId) internal view returns (address) {
                                                      return kernel().getApp(KERNEL_APP_BASES_NAMESPACE, _appId);
                                                  }
                                              }
                                              
                                              // File: contracts/apps/AppProxyUpgradeable.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              contract AppProxyUpgradeable is AppProxyBase {
                                                  /**
                                                  * @dev Initialize AppProxyUpgradeable (makes it an upgradeable Aragon app)
                                                  * @param _kernel Reference to organization kernel for the app
                                                  * @param _appId Identifier for app
                                                  * @param _initializePayload Payload for call to be made after setup to initialize
                                                  */
                                                  constructor(IKernel _kernel, bytes32 _appId, bytes _initializePayload)
                                                      AppProxyBase(_kernel, _appId, _initializePayload)
                                                      public // solium-disable-line visibility-first
                                                  {
                                                      // solium-disable-previous-line no-empty-blocks
                                                  }
                                              
                                                  /**
                                                   * @dev ERC897, the address the proxy would delegate calls to
                                                   */
                                                  function implementation() public view returns (address) {
                                                      return getAppBase(appId());
                                                  }
                                              
                                                  /**
                                                   * @dev ERC897, whether it is a forwarding (1) or an upgradeable (2) proxy
                                                   */
                                                  function proxyType() public pure returns (uint256 proxyTypeId) {
                                                      return UPGRADEABLE;
                                                  }
                                              }
                                              
                                              // File: contracts/apps/AppProxyPinned.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              contract AppProxyPinned is IsContract, AppProxyBase {
                                                  using UnstructuredStorage for bytes32;
                                              
                                                  // keccak256("aragonOS.appStorage.pinnedCode")
                                                  bytes32 internal constant PINNED_CODE_POSITION = 0xdee64df20d65e53d7f51cb6ab6d921a0a6a638a91e942e1d8d02df28e31c038e;
                                              
                                                  /**
                                                  * @dev Initialize AppProxyPinned (makes it an un-upgradeable Aragon app)
                                                  * @param _kernel Reference to organization kernel for the app
                                                  * @param _appId Identifier for app
                                                  * @param _initializePayload Payload for call to be made after setup to initialize
                                                  */
                                                  constructor(IKernel _kernel, bytes32 _appId, bytes _initializePayload)
                                                      AppProxyBase(_kernel, _appId, _initializePayload)
                                                      public // solium-disable-line visibility-first
                                                  {
                                                      setPinnedCode(getAppBase(_appId));
                                                      require(isContract(pinnedCode()));
                                                  }
                                              
                                                  /**
                                                   * @dev ERC897, the address the proxy would delegate calls to
                                                   */
                                                  function implementation() public view returns (address) {
                                                      return pinnedCode();
                                                  }
                                              
                                                  /**
                                                   * @dev ERC897, whether it is a forwarding (1) or an upgradeable (2) proxy
                                                   */
                                                  function proxyType() public pure returns (uint256 proxyTypeId) {
                                                      return FORWARDING;
                                                  }
                                              
                                                  function setPinnedCode(address _pinnedCode) internal {
                                                      PINNED_CODE_POSITION.setStorageAddress(_pinnedCode);
                                                  }
                                              
                                                  function pinnedCode() internal view returns (address) {
                                                      return PINNED_CODE_POSITION.getStorageAddress();
                                                  }
                                              }
                                              
                                              // File: contracts/factory/AppProxyFactory.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              contract AppProxyFactory {
                                                  event NewAppProxy(address proxy, bool isUpgradeable, bytes32 appId);
                                              
                                                  /**
                                                  * @notice Create a new upgradeable app instance on `_kernel` with identifier `_appId`
                                                  * @param _kernel App's Kernel reference
                                                  * @param _appId Identifier for app
                                                  * @return AppProxyUpgradeable
                                                  */
                                                  function newAppProxy(IKernel _kernel, bytes32 _appId) public returns (AppProxyUpgradeable) {
                                                      return newAppProxy(_kernel, _appId, new bytes(0));
                                                  }
                                              
                                                  /**
                                                  * @notice Create a new upgradeable app instance on `_kernel` with identifier `_appId` and initialization payload `_initializePayload`
                                                  * @param _kernel App's Kernel reference
                                                  * @param _appId Identifier for app
                                                  * @return AppProxyUpgradeable
                                                  */
                                                  function newAppProxy(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public returns (AppProxyUpgradeable) {
                                                      AppProxyUpgradeable proxy = new AppProxyUpgradeable(_kernel, _appId, _initializePayload);
                                                      emit NewAppProxy(address(proxy), true, _appId);
                                                      return proxy;
                                                  }
                                              
                                                  /**
                                                  * @notice Create a new pinned app instance on `_kernel` with identifier `_appId`
                                                  * @param _kernel App's Kernel reference
                                                  * @param _appId Identifier for app
                                                  * @return AppProxyPinned
                                                  */
                                                  function newAppProxyPinned(IKernel _kernel, bytes32 _appId) public returns (AppProxyPinned) {
                                                      return newAppProxyPinned(_kernel, _appId, new bytes(0));
                                                  }
                                              
                                                  /**
                                                  * @notice Create a new pinned app instance on `_kernel` with identifier `_appId` and initialization payload `_initializePayload`
                                                  * @param _kernel App's Kernel reference
                                                  * @param _appId Identifier for app
                                                  * @param _initializePayload Proxy initialization payload
                                                  * @return AppProxyPinned
                                                  */
                                                  function newAppProxyPinned(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public returns (AppProxyPinned) {
                                                      AppProxyPinned proxy = new AppProxyPinned(_kernel, _appId, _initializePayload);
                                                      emit NewAppProxy(address(proxy), false, _appId);
                                                      return proxy;
                                                  }
                                              }
                                              
                                              // File: contracts/kernel/Kernel.sol
                                              
                                              pragma solidity 0.4.24;
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              
                                              // solium-disable-next-line max-len
                                              contract Kernel is IKernel, KernelStorage, KernelAppIds, KernelNamespaceConstants, Petrifiable, IsContract, VaultRecoverable, AppProxyFactory, ACLSyntaxSugar {
                                                  /* Hardcoded constants to save gas
                                                  bytes32 public constant APP_MANAGER_ROLE = keccak256("APP_MANAGER_ROLE");
                                                  */
                                                  bytes32 public constant APP_MANAGER_ROLE = 0xb6d92708f3d4817afc106147d969e229ced5c46e65e0a5002a0d391287762bd0;
                                              
                                                  string private constant ERROR_APP_NOT_CONTRACT = "KERNEL_APP_NOT_CONTRACT";
                                                  string private constant ERROR_INVALID_APP_CHANGE = "KERNEL_INVALID_APP_CHANGE";
                                                  string private constant ERROR_AUTH_FAILED = "KERNEL_AUTH_FAILED";
                                              
                                                  /**
                                                  * @dev Constructor that allows the deployer to choose if the base instance should be petrified immediately.
                                                  * @param _shouldPetrify Immediately petrify this instance so that it can never be initialized
                                                  */
                                                  constructor(bool _shouldPetrify) public {
                                                      if (_shouldPetrify) {
                                                          petrify();
                                                      }
                                                  }
                                              
                                                  /**
                                                  * @dev Initialize can only be called once. It saves the block number in which it was initialized.
                                                  * @notice Initialize this kernel instance along with its ACL and set `_permissionsCreator` as the entity that can create other permissions
                                                  * @param _baseAcl Address of base ACL app
                                                  * @param _permissionsCreator Entity that will be given permission over createPermission
                                                  */
                                                  function initialize(IACL _baseAcl, address _permissionsCreator) public onlyInit {
                                                      initialized();
                                              
                                                      // Set ACL base
                                                      _setApp(KERNEL_APP_BASES_NAMESPACE, KERNEL_DEFAULT_ACL_APP_ID, _baseAcl);
                                              
                                                      // Create ACL instance and attach it as the default ACL app
                                                      IACL acl = IACL(newAppProxy(this, KERNEL_DEFAULT_ACL_APP_ID));
                                                      acl.initialize(_permissionsCreator);
                                                      _setApp(KERNEL_APP_ADDR_NAMESPACE, KERNEL_DEFAULT_ACL_APP_ID, acl);
                                              
                                                      recoveryVaultAppId = KERNEL_DEFAULT_VAULT_APP_ID;
                                                  }
                                              
                                                  /**
                                                  * @dev Create a new instance of an app linked to this kernel
                                                  * @notice Create a new upgradeable instance of `_appId` app linked to the Kernel, setting its code to `_appBase`
                                                  * @param _appId Identifier for app
                                                  * @param _appBase Address of the app's base implementation
                                                  * @return AppProxy instance
                                                  */
                                                  function newAppInstance(bytes32 _appId, address _appBase)
                                                      public
                                                      auth(APP_MANAGER_ROLE, arr(KERNEL_APP_BASES_NAMESPACE, _appId))
                                                      returns (ERCProxy appProxy)
                                                  {
                                                      return newAppInstance(_appId, _appBase, new bytes(0), false);
                                                  }
                                              
                                                  /**
                                                  * @dev Create a new instance of an app linked to this kernel and set its base
                                                  *      implementation if it was not already set
                                                  * @notice Create a new upgradeable instance of `_appId` app linked to the Kernel, setting its code to `_appBase`. `_setDefault ? 'Also sets it as the default app instance.':''`
                                                  * @param _appId Identifier for app
                                                  * @param _appBase Address of the app's base implementation
                                                  * @param _initializePayload Payload for call made by the proxy during its construction to initialize
                                                  * @param _setDefault Whether the app proxy app is the default one.
                                                  *        Useful when the Kernel needs to know of an instance of a particular app,
                                                  *        like Vault for escape hatch mechanism.
                                                  * @return AppProxy instance
                                                  */
                                                  function newAppInstance(bytes32 _appId, address _appBase, bytes _initializePayload, bool _setDefault)
                                                      public
                                                      auth(APP_MANAGER_ROLE, arr(KERNEL_APP_BASES_NAMESPACE, _appId))
                                                      returns (ERCProxy appProxy)
                                                  {
                                                      _setAppIfNew(KERNEL_APP_BASES_NAMESPACE, _appId, _appBase);
                                                      appProxy = newAppProxy(this, _appId, _initializePayload);
                                                      // By calling setApp directly and not the internal functions, we make sure the params are checked
                                                      // and it will only succeed if sender has permissions to set something to the namespace.
                                                      if (_setDefault) {
                                                          setApp(KERNEL_APP_ADDR_NAMESPACE, _appId, appProxy);
                                                      }
                                                  }
                                              
                                                  /**
                                                  * @dev Create a new pinned instance of an app linked to this kernel
                                                  * @notice Create a new non-upgradeable instance of `_appId` app linked to the Kernel, setting its code to `_appBase`.
                                                  * @param _appId Identifier for app
                                                  * @param _appBase Address of the app's base implementation
                                                  * @return AppProxy instance
                                                  */
                                                  function newPinnedAppInstance(bytes32 _appId, address _appBase)
                                                      public
                                                      auth(APP_MANAGER_ROLE, arr(KERNEL_APP_BASES_NAMESPACE, _appId))
                                                      returns (ERCProxy appProxy)
                                                  {
                                                      return newPinnedAppInstance(_appId, _appBase, new bytes(0), false);
                                                  }
                                              
                                                  /**
                                                  * @dev Create a new pinned instance of an app linked to this kernel and set
                                                  *      its base implementation if it was not already set
                                                  * @notice Create a new non-upgradeable instance of `_appId` app linked to the Kernel, setting its code to `_appBase`. `_setDefault ? 'Also sets it as the default app instance.':''`
                                                  * @param _appId Identifier for app
                                                  * @param _appBase Address of the app's base implementation
                                                  * @param _initializePayload Payload for call made by the proxy during its construction to initialize
                                                  * @param _setDefault Whether the app proxy app is the default one.
                                                  *        Useful when the Kernel needs to know of an instance of a particular app,
                                                  *        like Vault for escape hatch mechanism.
                                                  * @return AppProxy instance
                                                  */
                                                  function newPinnedAppInstance(bytes32 _appId, address _appBase, bytes _initializePayload, bool _setDefault)
                                                      public
                                                      auth(APP_MANAGER_ROLE, arr(KERNEL_APP_BASES_NAMESPACE, _appId))
                                                      returns (ERCProxy appProxy)
                                                  {
                                                      _setAppIfNew(KERNEL_APP_BASES_NAMESPACE, _appId, _appBase);
                                                      appProxy = newAppProxyPinned(this, _appId, _initializePayload);
                                                      // By calling setApp directly and not the internal functions, we make sure the params are checked
                                                      // and it will only succeed if sender has permissions to set something to the namespace.
                                                      if (_setDefault) {
                                                          setApp(KERNEL_APP_ADDR_NAMESPACE, _appId, appProxy);
                                                      }
                                                  }
                                              
                                                  /**
                                                  * @dev Set the resolving address of an app instance or base implementation
                                                  * @notice Set the resolving address of `_appId` in namespace `_namespace` to `_app`
                                                  * @param _namespace App namespace to use
                                                  * @param _appId Identifier for app
                                                  * @param _app Address of the app instance or base implementation
                                                  * @return ID of app
                                                  */
                                                  function setApp(bytes32 _namespace, bytes32 _appId, address _app)
                                                      public
                                                      auth(APP_MANAGER_ROLE, arr(_namespace, _appId))
                                                  {
                                                      _setApp(_namespace, _appId, _app);
                                                  }
                                              
                                                  /**
                                                  * @dev Set the default vault id for the escape hatch mechanism
                                                  * @param _recoveryVaultAppId Identifier of the recovery vault app
                                                  */
                                                  function setRecoveryVaultAppId(bytes32 _recoveryVaultAppId)
                                                      public
                                                      auth(APP_MANAGER_ROLE, arr(KERNEL_APP_ADDR_NAMESPACE, _recoveryVaultAppId))
                                                  {
                                                      recoveryVaultAppId = _recoveryVaultAppId;
                                                  }
                                              
                                                  // External access to default app id and namespace constants to mimic default getters for constants
                                                  /* solium-disable function-order, mixedcase */
                                                  function CORE_NAMESPACE() external pure returns (bytes32) { return KERNEL_CORE_NAMESPACE; }
                                                  function APP_BASES_NAMESPACE() external pure returns (bytes32) { return KERNEL_APP_BASES_NAMESPACE; }
                                                  function APP_ADDR_NAMESPACE() external pure returns (bytes32) { return KERNEL_APP_ADDR_NAMESPACE; }
                                                  function KERNEL_APP_ID() external pure returns (bytes32) { return KERNEL_CORE_APP_ID; }
                                                  function DEFAULT_ACL_APP_ID() external pure returns (bytes32) { return KERNEL_DEFAULT_ACL_APP_ID; }
                                                  /* solium-enable function-order, mixedcase */
                                              
                                                  /**
                                                  * @dev Get the address of an app instance or base implementation
                                                  * @param _namespace App namespace to use
                                                  * @param _appId Identifier for app
                                                  * @return Address of the app
                                                  */
                                                  function getApp(bytes32 _namespace, bytes32 _appId) public view returns (address) {
                                                      return apps[_namespace][_appId];
                                                  }
                                              
                                                  /**
                                                  * @dev Get the address of the recovery Vault instance (to recover funds)
                                                  * @return Address of the Vault
                                                  */
                                                  function getRecoveryVault() public view returns (address) {
                                                      return apps[KERNEL_APP_ADDR_NAMESPACE][recoveryVaultAppId];
                                                  }
                                              
                                                  /**
                                                  * @dev Get the installed ACL app
                                                  * @return ACL app
                                                  */
                                                  function acl() public view returns (IACL) {
                                                      return IACL(getApp(KERNEL_APP_ADDR_NAMESPACE, KERNEL_DEFAULT_ACL_APP_ID));
                                                  }
                                              
                                                  /**
                                                  * @dev Function called by apps to check ACL on kernel or to check permission status
                                                  * @param _who Sender of the original call
                                                  * @param _where Address of the app
                                                  * @param _what Identifier for a group of actions in app
                                                  * @param _how Extra data for ACL auth
                                                  * @return Boolean indicating whether the ACL allows the role or not.
                                                  *         Always returns false if the kernel hasn't been initialized yet.
                                                  */
                                                  function hasPermission(address _who, address _where, bytes32 _what, bytes _how) public view returns (bool) {
                                                      IACL defaultAcl = acl();
                                                      return address(defaultAcl) != address(0) && // Poor man's initialization check (saves gas)
                                                          defaultAcl.hasPermission(_who, _where, _what, _how);
                                                  }
                                              
                                                  function _setApp(bytes32 _namespace, bytes32 _appId, address _app) internal {
                                                      require(isContract(_app), ERROR_APP_NOT_CONTRACT);
                                                      apps[_namespace][_appId] = _app;
                                                      emit SetApp(_namespace, _appId, _app);
                                                  }
                                              
                                                  function _setAppIfNew(bytes32 _namespace, bytes32 _appId, address _app) internal {
                                                      address app = getApp(_namespace, _appId);
                                                      if (app != address(0)) {
                                                          // The only way to set an app is if it passes the isContract check, so no need to check it again
                                                          require(app == _app, ERROR_INVALID_APP_CHANGE);
                                                      } else {
                                                          _setApp(_namespace, _appId, _app);
                                                      }
                                                  }
                                              
                                                  modifier auth(bytes32 _role, uint256[] memory _params) {
                                                      require(
                                                          hasPermission(msg.sender, address(this), _role, ConversionHelpers.dangerouslyCastUintArrayToBytes(_params)),
                                                          ERROR_AUTH_FAILED
                                                      );
                                                      _;
                                                  }
                                              }

                                              File 6 of 6: Lido
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              contract ACLSyntaxSugar {
                                                  function arr() internal pure returns (uint256[]) {
                                                      return new uint256[](0);
                                                  }
                                                  function arr(bytes32 _a) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a));
                                                  }
                                                  function arr(bytes32 _a, bytes32 _b) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b));
                                                  }
                                                  function arr(address _a) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a));
                                                  }
                                                  function arr(address _a, address _b) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b));
                                                  }
                                                  function arr(address _a, uint256 _b, uint256 _c) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), _b, _c);
                                                  }
                                                  function arr(address _a, uint256 _b, uint256 _c, uint256 _d) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), _b, _c, _d);
                                                  }
                                                  function arr(address _a, uint256 _b) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b));
                                                  }
                                                  function arr(address _a, address _b, uint256 _c, uint256 _d, uint256 _e) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b), _c, _d, _e);
                                                  }
                                                  function arr(address _a, address _b, address _c) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b), uint256(_c));
                                                  }
                                                  function arr(address _a, address _b, uint256 _c) internal pure returns (uint256[] r) {
                                                      return arr(uint256(_a), uint256(_b), uint256(_c));
                                                  }
                                                  function arr(uint256 _a) internal pure returns (uint256[] r) {
                                                      r = new uint256[](1);
                                                      r[0] = _a;
                                                  }
                                                  function arr(uint256 _a, uint256 _b) internal pure returns (uint256[] r) {
                                                      r = new uint256[](2);
                                                      r[0] = _a;
                                                      r[1] = _b;
                                                  }
                                                  function arr(uint256 _a, uint256 _b, uint256 _c) internal pure returns (uint256[] r) {
                                                      r = new uint256[](3);
                                                      r[0] = _a;
                                                      r[1] = _b;
                                                      r[2] = _c;
                                                  }
                                                  function arr(uint256 _a, uint256 _b, uint256 _c, uint256 _d) internal pure returns (uint256[] r) {
                                                      r = new uint256[](4);
                                                      r[0] = _a;
                                                      r[1] = _b;
                                                      r[2] = _c;
                                                      r[3] = _d;
                                                  }
                                                  function arr(uint256 _a, uint256 _b, uint256 _c, uint256 _d, uint256 _e) internal pure returns (uint256[] r) {
                                                      r = new uint256[](5);
                                                      r[0] = _a;
                                                      r[1] = _b;
                                                      r[2] = _c;
                                                      r[3] = _d;
                                                      r[4] = _e;
                                                  }
                                              }
                                              contract ACLHelpers {
                                                  function decodeParamOp(uint256 _x) internal pure returns (uint8 b) {
                                                      return uint8(_x >> (8 * 30));
                                                  }
                                                  function decodeParamId(uint256 _x) internal pure returns (uint8 b) {
                                                      return uint8(_x >> (8 * 31));
                                                  }
                                                  function decodeParamsList(uint256 _x) internal pure returns (uint32 a, uint32 b, uint32 c) {
                                                      a = uint32(_x);
                                                      b = uint32(_x >> (8 * 4));
                                                      c = uint32(_x >> (8 * 8));
                                                  }
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              interface IACL {
                                                  function initialize(address permissionsCreator) external;
                                                  // TODO: this should be external
                                                  // See https://github.com/ethereum/solidity/issues/4832
                                                  function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              import "../common/UnstructuredStorage.sol";
                                              import "../kernel/IKernel.sol";
                                              contract AppStorage {
                                                  using UnstructuredStorage for bytes32;
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant KERNEL_POSITION = keccak256("aragonOS.appStorage.kernel");
                                                  bytes32 internal constant APP_ID_POSITION = keccak256("aragonOS.appStorage.appId");
                                                  */
                                                  bytes32 internal constant KERNEL_POSITION = 0x4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b;
                                                  bytes32 internal constant APP_ID_POSITION = 0xd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b;
                                                  function kernel() public view returns (IKernel) {
                                                      return IKernel(KERNEL_POSITION.getStorageAddress());
                                                  }
                                                  function appId() public view returns (bytes32) {
                                                      return APP_ID_POSITION.getStorageBytes32();
                                                  }
                                                  function setKernel(IKernel _kernel) internal {
                                                      KERNEL_POSITION.setStorageAddress(address(_kernel));
                                                  }
                                                  function setAppId(bytes32 _appId) internal {
                                                      APP_ID_POSITION.setStorageBytes32(_appId);
                                                  }
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              import "./AppStorage.sol";
                                              import "../acl/ACLSyntaxSugar.sol";
                                              import "../common/Autopetrified.sol";
                                              import "../common/ConversionHelpers.sol";
                                              import "../common/ReentrancyGuard.sol";
                                              import "../common/VaultRecoverable.sol";
                                              import "../evmscript/EVMScriptRunner.sol";
                                              // Contracts inheriting from AragonApp are, by default, immediately petrified upon deployment so
                                              // that they can never be initialized.
                                              // Unless overriden, this behaviour enforces those contracts to be usable only behind an AppProxy.
                                              // ReentrancyGuard, EVMScriptRunner, and ACLSyntaxSugar are not directly used by this contract, but
                                              // are included so that they are automatically usable by subclassing contracts
                                              contract AragonApp is AppStorage, Autopetrified, VaultRecoverable, ReentrancyGuard, EVMScriptRunner, ACLSyntaxSugar {
                                                  string private constant ERROR_AUTH_FAILED = "APP_AUTH_FAILED";
                                                  modifier auth(bytes32 _role) {
                                                      require(canPerform(msg.sender, _role, new uint256[](0)), ERROR_AUTH_FAILED);
                                                      _;
                                                  }
                                                  modifier authP(bytes32 _role, uint256[] _params) {
                                                      require(canPerform(msg.sender, _role, _params), ERROR_AUTH_FAILED);
                                                      _;
                                                  }
                                                  /**
                                                  * @dev Check whether an action can be performed by a sender for a particular role on this app
                                                  * @param _sender Sender of the call
                                                  * @param _role Role on this app
                                                  * @param _params Permission params for the role
                                                  * @return Boolean indicating whether the sender has the permissions to perform the action.
                                                  *         Always returns false if the app hasn't been initialized yet.
                                                  */
                                                  function canPerform(address _sender, bytes32 _role, uint256[] _params) public view returns (bool) {
                                                      if (!hasInitialized()) {
                                                          return false;
                                                      }
                                                      IKernel linkedKernel = kernel();
                                                      if (address(linkedKernel) == address(0)) {
                                                          return false;
                                                      }
                                                      return linkedKernel.hasPermission(
                                                          _sender,
                                                          address(this),
                                                          _role,
                                                          ConversionHelpers.dangerouslyCastUintArrayToBytes(_params)
                                                      );
                                                  }
                                                  /**
                                                  * @dev Get the recovery vault for the app
                                                  * @return Recovery vault address for the app
                                                  */
                                                  function getRecoveryVault() public view returns (address) {
                                                      // Funds recovery via a vault is only available when used with a kernel
                                                      return kernel().getRecoveryVault(); // if kernel is not set, it will revert
                                                  }
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              import "./Petrifiable.sol";
                                              contract Autopetrified is Petrifiable {
                                                  constructor() public {
                                                      // Immediately petrify base (non-proxy) instances of inherited contracts on deploy.
                                                      // This renders them uninitializable (and unusable without a proxy).
                                                      petrify();
                                                  }
                                              }
                                              pragma solidity ^0.4.24;
                                              library ConversionHelpers {
                                                  string private constant ERROR_IMPROPER_LENGTH = "CONVERSION_IMPROPER_LENGTH";
                                                  function dangerouslyCastUintArrayToBytes(uint256[] memory _input) internal pure returns (bytes memory output) {
                                                      // Force cast the uint256[] into a bytes array, by overwriting its length
                                                      // Note that the bytes array doesn't need to be initialized as we immediately overwrite it
                                                      // with the input and a new length. The input becomes invalid from this point forward.
                                                      uint256 byteLength = _input.length * 32;
                                                      assembly {
                                                          output := _input
                                                          mstore(output, byteLength)
                                                      }
                                                  }
                                                  function dangerouslyCastBytesToUintArray(bytes memory _input) internal pure returns (uint256[] memory output) {
                                                      // Force cast the bytes array into a uint256[], by overwriting its length
                                                      // Note that the uint256[] doesn't need to be initialized as we immediately overwrite it
                                                      // with the input and a new length. The input becomes invalid from this point forward.
                                                      uint256 intsLength = _input.length / 32;
                                                      require(_input.length == intsLength * 32, ERROR_IMPROPER_LENGTH);
                                                      assembly {
                                                          output := _input
                                                          mstore(output, intsLength)
                                                      }
                                                  }
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              // aragonOS and aragon-apps rely on address(0) to denote native ETH, in
                                              // contracts where both tokens and ETH are accepted
                                              contract EtherTokenConstant {
                                                  address internal constant ETH = address(0);
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              import "./TimeHelpers.sol";
                                              import "./UnstructuredStorage.sol";
                                              contract Initializable is TimeHelpers {
                                                  using UnstructuredStorage for bytes32;
                                                  // keccak256("aragonOS.initializable.initializationBlock")
                                                  bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e;
                                                  string private constant ERROR_ALREADY_INITIALIZED = "INIT_ALREADY_INITIALIZED";
                                                  string private constant ERROR_NOT_INITIALIZED = "INIT_NOT_INITIALIZED";
                                                  modifier onlyInit {
                                                      require(getInitializationBlock() == 0, ERROR_ALREADY_INITIALIZED);
                                                      _;
                                                  }
                                                  modifier isInitialized {
                                                      require(hasInitialized(), ERROR_NOT_INITIALIZED);
                                                      _;
                                                  }
                                                  /**
                                                  * @return Block number in which the contract was initialized
                                                  */
                                                  function getInitializationBlock() public view returns (uint256) {
                                                      return INITIALIZATION_BLOCK_POSITION.getStorageUint256();
                                                  }
                                                  /**
                                                  * @return Whether the contract has been initialized by the time of the current block
                                                  */
                                                  function hasInitialized() public view returns (bool) {
                                                      uint256 initializationBlock = getInitializationBlock();
                                                      return initializationBlock != 0 && getBlockNumber() >= initializationBlock;
                                                  }
                                                  /**
                                                  * @dev Function to be called by top level contract after initialization has finished.
                                                  */
                                                  function initialized() internal onlyInit {
                                                      INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber());
                                                  }
                                                  /**
                                                  * @dev Function to be called by top level contract after initialization to enable the contract
                                                  *      at a future block number rather than immediately.
                                                  */
                                                  function initializedAt(uint256 _blockNumber) internal onlyInit {
                                                      INITIALIZATION_BLOCK_POSITION.setStorageUint256(_blockNumber);
                                                  }
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              contract IsContract {
                                                  /*
                                                  * NOTE: this should NEVER be used for authentication
                                                  * (see pitfalls: https://github.com/fergarrui/ethereum-security/tree/master/contracts/extcodesize).
                                                  *
                                                  * This is only intended to be used as a sanity check that an address is actually a contract,
                                                  * RATHER THAN an address not being a contract.
                                                  */
                                                  function isContract(address _target) internal view returns (bool) {
                                                      if (_target == address(0)) {
                                                          return false;
                                                      }
                                                      uint256 size;
                                                      assembly { size := extcodesize(_target) }
                                                      return size > 0;
                                                  }
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              interface IVaultRecoverable {
                                                  event RecoverToVault(address indexed vault, address indexed token, uint256 amount);
                                                  function transferToVault(address token) external;
                                                  function allowRecoverability(address token) external view returns (bool);
                                                  function getRecoveryVault() external view returns (address);
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              import "./Initializable.sol";
                                              contract Petrifiable is Initializable {
                                                  // Use block UINT256_MAX (which should be never) as the initializable date
                                                  uint256 internal constant PETRIFIED_BLOCK = uint256(-1);
                                                  function isPetrified() public view returns (bool) {
                                                      return getInitializationBlock() == PETRIFIED_BLOCK;
                                                  }
                                                  /**
                                                  * @dev Function to be called by top level contract to prevent being initialized.
                                                  *      Useful for freezing base contracts when they're used behind proxies.
                                                  */
                                                  function petrify() internal onlyInit {
                                                      initializedAt(PETRIFIED_BLOCK);
                                                  }
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              import "../common/UnstructuredStorage.sol";
                                              contract ReentrancyGuard {
                                                  using UnstructuredStorage for bytes32;
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant REENTRANCY_MUTEX_POSITION = keccak256("aragonOS.reentrancyGuard.mutex");
                                                  */
                                                  bytes32 private constant REENTRANCY_MUTEX_POSITION = 0xe855346402235fdd185c890e68d2c4ecad599b88587635ee285bce2fda58dacb;
                                                  string private constant ERROR_REENTRANT = "REENTRANCY_REENTRANT_CALL";
                                                  modifier nonReentrant() {
                                                      // Ensure mutex is unlocked
                                                      require(!REENTRANCY_MUTEX_POSITION.getStorageBool(), ERROR_REENTRANT);
                                                      // Lock mutex before function call
                                                      REENTRANCY_MUTEX_POSITION.setStorageBool(true);
                                                      // Perform function call
                                                      _;
                                                      // Unlock mutex after function call
                                                      REENTRANCY_MUTEX_POSITION.setStorageBool(false);
                                                  }
                                              }
                                              // Inspired by AdEx (https://github.com/AdExNetwork/adex-protocol-eth/blob/b9df617829661a7518ee10f4cb6c4108659dd6d5/contracts/libs/SafeERC20.sol)
                                              // and 0x (https://github.com/0xProject/0x-monorepo/blob/737d1dc54d72872e24abce5a1dbe1b66d35fa21a/contracts/protocol/contracts/protocol/AssetProxy/ERC20Proxy.sol#L143)
                                              pragma solidity ^0.4.24;
                                              import "../lib/token/ERC20.sol";
                                              library SafeERC20 {
                                                  // Before 0.5, solidity has a mismatch between `address.transfer()` and `token.transfer()`:
                                                  // https://github.com/ethereum/solidity/issues/3544
                                                  bytes4 private constant TRANSFER_SELECTOR = 0xa9059cbb;
                                                  string private constant ERROR_TOKEN_BALANCE_REVERTED = "SAFE_ERC_20_BALANCE_REVERTED";
                                                  string private constant ERROR_TOKEN_ALLOWANCE_REVERTED = "SAFE_ERC_20_ALLOWANCE_REVERTED";
                                                  function invokeAndCheckSuccess(address _addr, bytes memory _calldata)
                                                      private
                                                      returns (bool)
                                                  {
                                                      bool ret;
                                                      assembly {
                                                          let ptr := mload(0x40)    // free memory pointer
                                                          let success := call(
                                                              gas,                  // forward all gas
                                                              _addr,                // address
                                                              0,                    // no value
                                                              add(_calldata, 0x20), // calldata start
                                                              mload(_calldata),     // calldata length
                                                              ptr,                  // write output over free memory
                                                              0x20                  // uint256 return
                                                          )
                                                          if gt(success, 0) {
                                                              // Check number of bytes returned from last function call
                                                              switch returndatasize
                                                              // No bytes returned: assume success
                                                              case 0 {
                                                                  ret := 1
                                                              }
                                                              // 32 bytes returned: check if non-zero
                                                              case 0x20 {
                                                                  // Only return success if returned data was true
                                                                  // Already have output in ptr
                                                                  ret := eq(mload(ptr), 1)
                                                              }
                                                              // Not sure what was returned: don't mark as success
                                                              default { }
                                                          }
                                                      }
                                                      return ret;
                                                  }
                                                  function staticInvoke(address _addr, bytes memory _calldata)
                                                      private
                                                      view
                                                      returns (bool, uint256)
                                                  {
                                                      bool success;
                                                      uint256 ret;
                                                      assembly {
                                                          let ptr := mload(0x40)    // free memory pointer
                                                          success := staticcall(
                                                              gas,                  // forward all gas
                                                              _addr,                // address
                                                              add(_calldata, 0x20), // calldata start
                                                              mload(_calldata),     // calldata length
                                                              ptr,                  // write output over free memory
                                                              0x20                  // uint256 return
                                                          )
                                                          if gt(success, 0) {
                                                              ret := mload(ptr)
                                                          }
                                                      }
                                                      return (success, ret);
                                                  }
                                                  /**
                                                  * @dev Same as a standards-compliant ERC20.transfer() that never reverts (returns false).
                                                  *      Note that this makes an external call to the token.
                                                  */
                                                  function safeTransfer(ERC20 _token, address _to, uint256 _amount) internal returns (bool) {
                                                      bytes memory transferCallData = abi.encodeWithSelector(
                                                          TRANSFER_SELECTOR,
                                                          _to,
                                                          _amount
                                                      );
                                                      return invokeAndCheckSuccess(_token, transferCallData);
                                                  }
                                                  /**
                                                  * @dev Same as a standards-compliant ERC20.transferFrom() that never reverts (returns false).
                                                  *      Note that this makes an external call to the token.
                                                  */
                                                  function safeTransferFrom(ERC20 _token, address _from, address _to, uint256 _amount) internal returns (bool) {
                                                      bytes memory transferFromCallData = abi.encodeWithSelector(
                                                          _token.transferFrom.selector,
                                                          _from,
                                                          _to,
                                                          _amount
                                                      );
                                                      return invokeAndCheckSuccess(_token, transferFromCallData);
                                                  }
                                                  /**
                                                  * @dev Same as a standards-compliant ERC20.approve() that never reverts (returns false).
                                                  *      Note that this makes an external call to the token.
                                                  */
                                                  function safeApprove(ERC20 _token, address _spender, uint256 _amount) internal returns (bool) {
                                                      bytes memory approveCallData = abi.encodeWithSelector(
                                                          _token.approve.selector,
                                                          _spender,
                                                          _amount
                                                      );
                                                      return invokeAndCheckSuccess(_token, approveCallData);
                                                  }
                                                  /**
                                                  * @dev Static call into ERC20.balanceOf().
                                                  * Reverts if the call fails for some reason (should never fail).
                                                  */
                                                  function staticBalanceOf(ERC20 _token, address _owner) internal view returns (uint256) {
                                                      bytes memory balanceOfCallData = abi.encodeWithSelector(
                                                          _token.balanceOf.selector,
                                                          _owner
                                                      );
                                                      (bool success, uint256 tokenBalance) = staticInvoke(_token, balanceOfCallData);
                                                      require(success, ERROR_TOKEN_BALANCE_REVERTED);
                                                      return tokenBalance;
                                                  }
                                                  /**
                                                  * @dev Static call into ERC20.allowance().
                                                  * Reverts if the call fails for some reason (should never fail).
                                                  */
                                                  function staticAllowance(ERC20 _token, address _owner, address _spender) internal view returns (uint256) {
                                                      bytes memory allowanceCallData = abi.encodeWithSelector(
                                                          _token.allowance.selector,
                                                          _owner,
                                                          _spender
                                                      );
                                                      (bool success, uint256 allowance) = staticInvoke(_token, allowanceCallData);
                                                      require(success, ERROR_TOKEN_ALLOWANCE_REVERTED);
                                                      return allowance;
                                                  }
                                                  /**
                                                  * @dev Static call into ERC20.totalSupply().
                                                  * Reverts if the call fails for some reason (should never fail).
                                                  */
                                                  function staticTotalSupply(ERC20 _token) internal view returns (uint256) {
                                                      bytes memory totalSupplyCallData = abi.encodeWithSelector(_token.totalSupply.selector);
                                                      (bool success, uint256 totalSupply) = staticInvoke(_token, totalSupplyCallData);
                                                      require(success, ERROR_TOKEN_ALLOWANCE_REVERTED);
                                                      return totalSupply;
                                                  }
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              import "./Uint256Helpers.sol";
                                              contract TimeHelpers {
                                                  using Uint256Helpers for uint256;
                                                  /**
                                                  * @dev Returns the current block number.
                                                  *      Using a function rather than `block.number` allows us to easily mock the block number in
                                                  *      tests.
                                                  */
                                                  function getBlockNumber() internal view returns (uint256) {
                                                      return block.number;
                                                  }
                                                  /**
                                                  * @dev Returns the current block number, converted to uint64.
                                                  *      Using a function rather than `block.number` allows us to easily mock the block number in
                                                  *      tests.
                                                  */
                                                  function getBlockNumber64() internal view returns (uint64) {
                                                      return getBlockNumber().toUint64();
                                                  }
                                                  /**
                                                  * @dev Returns the current timestamp.
                                                  *      Using a function rather than `block.timestamp` allows us to easily mock it in
                                                  *      tests.
                                                  */
                                                  function getTimestamp() internal view returns (uint256) {
                                                      return block.timestamp; // solium-disable-line security/no-block-members
                                                  }
                                                  /**
                                                  * @dev Returns the current timestamp, converted to uint64.
                                                  *      Using a function rather than `block.timestamp` allows us to easily mock it in
                                                  *      tests.
                                                  */
                                                  function getTimestamp64() internal view returns (uint64) {
                                                      return getTimestamp().toUint64();
                                                  }
                                              }
                                              pragma solidity ^0.4.24;
                                              library Uint256Helpers {
                                                  uint256 private constant MAX_UINT64 = uint64(-1);
                                                  string private constant ERROR_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG";
                                                  function toUint64(uint256 a) internal pure returns (uint64) {
                                                      require(a <= MAX_UINT64, ERROR_NUMBER_TOO_BIG);
                                                      return uint64(a);
                                                  }
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              library UnstructuredStorage {
                                                  function getStorageBool(bytes32 position) internal view returns (bool data) {
                                                      assembly { data := sload(position) }
                                                  }
                                                  function getStorageAddress(bytes32 position) internal view returns (address data) {
                                                      assembly { data := sload(position) }
                                                  }
                                                  function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) {
                                                      assembly { data := sload(position) }
                                                  }
                                                  function getStorageUint256(bytes32 position) internal view returns (uint256 data) {
                                                      assembly { data := sload(position) }
                                                  }
                                                  function setStorageBool(bytes32 position, bool data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                                  function setStorageAddress(bytes32 position, address data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                                  function setStorageBytes32(bytes32 position, bytes32 data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                                  function setStorageUint256(bytes32 position, uint256 data) internal {
                                                      assembly { sstore(position, data) }
                                                  }
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              import "../lib/token/ERC20.sol";
                                              import "./EtherTokenConstant.sol";
                                              import "./IsContract.sol";
                                              import "./IVaultRecoverable.sol";
                                              import "./SafeERC20.sol";
                                              contract VaultRecoverable is IVaultRecoverable, EtherTokenConstant, IsContract {
                                                  using SafeERC20 for ERC20;
                                                  string private constant ERROR_DISALLOWED = "RECOVER_DISALLOWED";
                                                  string private constant ERROR_VAULT_NOT_CONTRACT = "RECOVER_VAULT_NOT_CONTRACT";
                                                  string private constant ERROR_TOKEN_TRANSFER_FAILED = "RECOVER_TOKEN_TRANSFER_FAILED";
                                                  /**
                                                   * @notice Send funds to recovery Vault. This contract should never receive funds,
                                                   *         but in case it does, this function allows one to recover them.
                                                   * @param _token Token balance to be sent to recovery vault.
                                                   */
                                                  function transferToVault(address _token) external {
                                                      require(allowRecoverability(_token), ERROR_DISALLOWED);
                                                      address vault = getRecoveryVault();
                                                      require(isContract(vault), ERROR_VAULT_NOT_CONTRACT);
                                                      uint256 balance;
                                                      if (_token == ETH) {
                                                          balance = address(this).balance;
                                                          vault.transfer(balance);
                                                      } else {
                                                          ERC20 token = ERC20(_token);
                                                          balance = token.staticBalanceOf(this);
                                                          require(token.safeTransfer(vault, balance), ERROR_TOKEN_TRANSFER_FAILED);
                                                      }
                                                      emit RecoverToVault(vault, _token, balance);
                                                  }
                                                  /**
                                                  * @dev By default deriving from AragonApp makes it recoverable
                                                  * @param token Token address that would be recovered
                                                  * @return bool whether the app allows the recovery
                                                  */
                                                  function allowRecoverability(address token) public view returns (bool) {
                                                      return true;
                                                  }
                                                  // Cast non-implemented interface to be public so we can use it internally
                                                  function getRecoveryVault() public view returns (address);
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              import "./IEVMScriptExecutor.sol";
                                              import "./IEVMScriptRegistry.sol";
                                              import "../apps/AppStorage.sol";
                                              import "../kernel/KernelConstants.sol";
                                              import "../common/Initializable.sol";
                                              contract EVMScriptRunner is AppStorage, Initializable, EVMScriptRegistryConstants, KernelNamespaceConstants {
                                                  string private constant ERROR_EXECUTOR_UNAVAILABLE = "EVMRUN_EXECUTOR_UNAVAILABLE";
                                                  string private constant ERROR_PROTECTED_STATE_MODIFIED = "EVMRUN_PROTECTED_STATE_MODIFIED";
                                                  /* This is manually crafted in assembly
                                                  string private constant ERROR_EXECUTOR_INVALID_RETURN = "EVMRUN_EXECUTOR_INVALID_RETURN";
                                                  */
                                                  event ScriptResult(address indexed executor, bytes script, bytes input, bytes returnData);
                                                  function getEVMScriptExecutor(bytes _script) public view returns (IEVMScriptExecutor) {
                                                      return IEVMScriptExecutor(getEVMScriptRegistry().getScriptExecutor(_script));
                                                  }
                                                  function getEVMScriptRegistry() public view returns (IEVMScriptRegistry) {
                                                      address registryAddr = kernel().getApp(KERNEL_APP_ADDR_NAMESPACE, EVMSCRIPT_REGISTRY_APP_ID);
                                                      return IEVMScriptRegistry(registryAddr);
                                                  }
                                                  function runScript(bytes _script, bytes _input, address[] _blacklist)
                                                      internal
                                                      isInitialized
                                                      protectState
                                                      returns (bytes)
                                                  {
                                                      IEVMScriptExecutor executor = getEVMScriptExecutor(_script);
                                                      require(address(executor) != address(0), ERROR_EXECUTOR_UNAVAILABLE);
                                                      bytes4 sig = executor.execScript.selector;
                                                      bytes memory data = abi.encodeWithSelector(sig, _script, _input, _blacklist);
                                                      bytes memory output;
                                                      assembly {
                                                          let success := delegatecall(
                                                              gas,                // forward all gas
                                                              executor,           // address
                                                              add(data, 0x20),    // calldata start
                                                              mload(data),        // calldata length
                                                              0,                  // don't write output (we'll handle this ourselves)
                                                              0                   // don't write output
                                                          )
                                                          output := mload(0x40) // free mem ptr get
                                                          switch success
                                                          case 0 {
                                                              // If the call errored, forward its full error data
                                                              returndatacopy(output, 0, returndatasize)
                                                              revert(output, returndatasize)
                                                          }
                                                          default {
                                                              switch gt(returndatasize, 0x3f)
                                                              case 0 {
                                                                  // Need at least 0x40 bytes returned for properly ABI-encoded bytes values,
                                                                  // revert with "EVMRUN_EXECUTOR_INVALID_RETURN"
                                                                  // See remix: doing a `revert("EVMRUN_EXECUTOR_INVALID_RETURN")` always results in
                                                                  // this memory layout
                                                                  mstore(output, 0x08c379a000000000000000000000000000000000000000000000000000000000)         // error identifier
                                                                  mstore(add(output, 0x04), 0x0000000000000000000000000000000000000000000000000000000000000020) // starting offset
                                                                  mstore(add(output, 0x24), 0x000000000000000000000000000000000000000000000000000000000000001e) // reason length
                                                                  mstore(add(output, 0x44), 0x45564d52554e5f4558454355544f525f494e56414c49445f52455455524e0000) // reason
                                                                  revert(output, 100) // 100 = 4 + 3 * 32 (error identifier + 3 words for the ABI encoded error)
                                                              }
                                                              default {
                                                                  // Copy result
                                                                  //
                                                                  // Needs to perform an ABI decode for the expected `bytes` return type of
                                                                  // `executor.execScript()` as solidity will automatically ABI encode the returned bytes as:
                                                                  //    [ position of the first dynamic length return value = 0x20 (32 bytes) ]
                                                                  //    [ output length (32 bytes) ]
                                                                  //    [ output content (N bytes) ]
                                                                  //
                                                                  // Perform the ABI decode by ignoring the first 32 bytes of the return data
                                                                  let copysize := sub(returndatasize, 0x20)
                                                                  returndatacopy(output, 0x20, copysize)
                                                                  mstore(0x40, add(output, copysize)) // free mem ptr set
                                                              }
                                                          }
                                                      }
                                                      emit ScriptResult(address(executor), _script, _input, output);
                                                      return output;
                                                  }
                                                  modifier protectState {
                                                      address preKernel = address(kernel());
                                                      bytes32 preAppId = appId();
                                                      _; // exec
                                                      require(address(kernel()) == preKernel, ERROR_PROTECTED_STATE_MODIFIED);
                                                      require(appId() == preAppId, ERROR_PROTECTED_STATE_MODIFIED);
                                                  }
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              interface IEVMScriptExecutor {
                                                  function execScript(bytes script, bytes input, address[] blacklist) external returns (bytes);
                                                  function executorType() external pure returns (bytes32);
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              import "./IEVMScriptExecutor.sol";
                                              contract EVMScriptRegistryConstants {
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant EVMSCRIPT_REGISTRY_APP_ID = apmNamehash("evmreg");
                                                  */
                                                  bytes32 internal constant EVMSCRIPT_REGISTRY_APP_ID = 0xddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd61;
                                              }
                                              interface IEVMScriptRegistry {
                                                  function addScriptExecutor(IEVMScriptExecutor executor) external returns (uint id);
                                                  function disableScriptExecutor(uint256 executorId) external;
                                                  // TODO: this should be external
                                                  // See https://github.com/ethereum/solidity/issues/4832
                                                  function getScriptExecutor(bytes script) public view returns (IEVMScriptExecutor);
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              import "../acl/IACL.sol";
                                              import "../common/IVaultRecoverable.sol";
                                              interface IKernelEvents {
                                                  event SetApp(bytes32 indexed namespace, bytes32 indexed appId, address app);
                                              }
                                              // This should be an interface, but interfaces can't inherit yet :(
                                              contract IKernel is IKernelEvents, IVaultRecoverable {
                                                  function acl() public view returns (IACL);
                                                  function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);
                                                  function setApp(bytes32 namespace, bytes32 appId, address app) public;
                                                  function getApp(bytes32 namespace, bytes32 appId) public view returns (address);
                                              }
                                              /*
                                               * SPDX-License-Identifier:    MIT
                                               */
                                              pragma solidity ^0.4.24;
                                              contract KernelAppIds {
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant KERNEL_CORE_APP_ID = apmNamehash("kernel");
                                                  bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = apmNamehash("acl");
                                                  bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = apmNamehash("vault");
                                                  */
                                                  bytes32 internal constant KERNEL_CORE_APP_ID = 0x3b4bf6bf3ad5000ecf0f989d5befde585c6860fea3e574a4fab4c49d1c177d9c;
                                                  bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = 0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a;
                                                  bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = 0x7e852e0fcfce6551c13800f1e7476f982525c2b5277ba14b24339c68416336d1;
                                              }
                                              contract KernelNamespaceConstants {
                                                  /* Hardcoded constants to save gas
                                                  bytes32 internal constant KERNEL_CORE_NAMESPACE = keccak256("core");
                                                  bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = keccak256("base");
                                                  bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = keccak256("app");
                                                  */
                                                  bytes32 internal constant KERNEL_CORE_NAMESPACE = 0xc681a85306374a5ab27f0bbc385296a54bcd314a1948b6cf61c4ea1bc44bb9f8;
                                                  bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = 0xf1f3eb40f5bc1ad1344716ced8b8a0431d840b5783aea1fd01786bc26f35ac0f;
                                                  bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
                                              }
                                              // See https://github.com/OpenZeppelin/openzeppelin-solidity/blob/d51e38758e1d985661534534d5c61e27bece5042/contracts/math/SafeMath.sol
                                              // Adapted to use pragma ^0.4.24 and satisfy our linter rules
                                              pragma solidity ^0.4.24;
                                              /**
                                               * @title SafeMath
                                               * @dev Math operations with safety checks that revert on error
                                               */
                                              library SafeMath {
                                                  string private constant ERROR_ADD_OVERFLOW = "MATH_ADD_OVERFLOW";
                                                  string private constant ERROR_SUB_UNDERFLOW = "MATH_SUB_UNDERFLOW";
                                                  string private constant ERROR_MUL_OVERFLOW = "MATH_MUL_OVERFLOW";
                                                  string private constant ERROR_DIV_ZERO = "MATH_DIV_ZERO";
                                                  /**
                                                  * @dev Multiplies two numbers, reverts on overflow.
                                                  */
                                                  function mul(uint256 _a, uint256 _b) internal pure returns (uint256) {
                                                      // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                                                      // benefit is lost if 'b' is also tested.
                                                      // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                                                      if (_a == 0) {
                                                          return 0;
                                                      }
                                                      uint256 c = _a * _b;
                                                      require(c / _a == _b, ERROR_MUL_OVERFLOW);
                                                      return c;
                                                  }
                                                  /**
                                                  * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
                                                  */
                                                  function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
                                                      require(_b > 0, ERROR_DIV_ZERO); // Solidity only automatically asserts when dividing by 0
                                                      uint256 c = _a / _b;
                                                      // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
                                                      return c;
                                                  }
                                                  /**
                                                  * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                                                  */
                                                  function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
                                                      require(_b <= _a, ERROR_SUB_UNDERFLOW);
                                                      uint256 c = _a - _b;
                                                      return c;
                                                  }
                                                  /**
                                                  * @dev Adds two numbers, reverts on overflow.
                                                  */
                                                  function add(uint256 _a, uint256 _b) internal pure returns (uint256) {
                                                      uint256 c = _a + _b;
                                                      require(c >= _a, ERROR_ADD_OVERFLOW);
                                                      return c;
                                                  }
                                                  /**
                                                  * @dev Divides two numbers and returns the remainder (unsigned integer modulo),
                                                  * reverts when dividing by zero.
                                                  */
                                                  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                                                      require(b != 0, ERROR_DIV_ZERO);
                                                      return a % b;
                                                  }
                                              }
                                              // See https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a9f910d34f0ab33a1ae5e714f69f9596a02b4d91/contracts/token/ERC20/ERC20.sol
                                              pragma solidity ^0.4.24;
                                              /**
                                               * @title ERC20 interface
                                               * @dev see https://github.com/ethereum/EIPs/issues/20
                                               */
                                              contract ERC20 {
                                                  function totalSupply() public view returns (uint256);
                                                  function balanceOf(address _who) public view returns (uint256);
                                                  function allowance(address _owner, address _spender)
                                                      public view returns (uint256);
                                                  function transfer(address _to, uint256 _value) public returns (bool);
                                                  function approve(address _spender, uint256 _value)
                                                      public returns (bool);
                                                  function transferFrom(address _from, address _to, uint256 _value)
                                                      public returns (bool);
                                                  event Transfer(
                                                      address indexed from,
                                                      address indexed to,
                                                      uint256 value
                                                  );
                                                  event Approval(
                                                      address indexed owner,
                                                      address indexed spender,
                                                      uint256 value
                                                  );
                                              }
                                              // SPDX-FileCopyrightText: 2023 Lido <[email protected]>
                                              // SPDX-License-Identifier: GPL-3.0
                                              /* See contracts/COMPILERS.md */
                                              pragma solidity 0.4.24;
                                              import "@aragon/os/contracts/common/UnstructuredStorage.sol";
                                              //
                                              // We need to pack four variables into the same 256bit-wide storage slot
                                              // to lower the costs per each staking request.
                                              //
                                              // As a result, slot's memory aligned as follows:
                                              //
                                              // MSB ------------------------------------------------------------------------------> LSB
                                              // 256____________160_________________________128_______________32_____________________ 0
                                              // |_______________|___________________________|________________|_______________________|
                                              // | maxStakeLimit | maxStakeLimitGrowthBlocks | prevStakeLimit | prevStakeBlockNumber  |
                                              // |<-- 96 bits -->|<---------- 32 bits ------>|<-- 96 bits --->|<----- 32 bits ------->|
                                              //
                                              //
                                              // NB: Internal representation conventions:
                                              //
                                              // - the `maxStakeLimitGrowthBlocks` field above represented as follows:
                                              // `maxStakeLimitGrowthBlocks` = `maxStakeLimit` / `stakeLimitIncreasePerBlock`
                                              //           32 bits                 96 bits               96 bits
                                              //
                                              //
                                              // - the "staking paused" state is encoded by `prevStakeBlockNumber` being zero,
                                              // - the "staking unlimited" state is encoded by `maxStakeLimit` being zero and `prevStakeBlockNumber` being non-zero.
                                              //
                                              /**
                                              * @notice Library for the internal structs definitions
                                              * @dev solidity <0.6 doesn't support top-level structs
                                              * using the library to have a proper namespace
                                              */
                                              library StakeLimitState {
                                                  /**
                                                    * @dev Internal representation struct (slot-wide)
                                                    */
                                                  struct Data {
                                                      uint32 prevStakeBlockNumber;      // block number of the previous stake submit
                                                      uint96 prevStakeLimit;            // limit value (<= `maxStakeLimit`) obtained on the previous stake submit
                                                      uint32 maxStakeLimitGrowthBlocks; // limit regeneration speed expressed in blocks
                                                      uint96 maxStakeLimit;             // maximum limit value
                                                  }
                                              }
                                              library StakeLimitUnstructuredStorage {
                                                  using UnstructuredStorage for bytes32;
                                                  /// @dev Storage offset for `maxStakeLimit` (bits)
                                                  uint256 internal constant MAX_STAKE_LIMIT_OFFSET = 160;
                                                  /// @dev Storage offset for `maxStakeLimitGrowthBlocks` (bits)
                                                  uint256 internal constant MAX_STAKE_LIMIT_GROWTH_BLOCKS_OFFSET = 128;
                                                  /// @dev Storage offset for `prevStakeLimit` (bits)
                                                  uint256 internal constant PREV_STAKE_LIMIT_OFFSET = 32;
                                                  /// @dev Storage offset for `prevStakeBlockNumber` (bits)
                                                  uint256 internal constant PREV_STAKE_BLOCK_NUMBER_OFFSET = 0;
                                                  /**
                                                  * @dev Read stake limit state from the unstructured storage position
                                                  * @param _position storage offset
                                                  */
                                                  function getStorageStakeLimitStruct(bytes32 _position) internal view returns (StakeLimitState.Data memory stakeLimit) {
                                                      uint256 slotValue = _position.getStorageUint256();
                                                      stakeLimit.prevStakeBlockNumber = uint32(slotValue >> PREV_STAKE_BLOCK_NUMBER_OFFSET);
                                                      stakeLimit.prevStakeLimit = uint96(slotValue >> PREV_STAKE_LIMIT_OFFSET);
                                                      stakeLimit.maxStakeLimitGrowthBlocks = uint32(slotValue >> MAX_STAKE_LIMIT_GROWTH_BLOCKS_OFFSET);
                                                      stakeLimit.maxStakeLimit = uint96(slotValue >> MAX_STAKE_LIMIT_OFFSET);
                                                  }
                                                   /**
                                                  * @dev Write stake limit state to the unstructured storage position
                                                  * @param _position storage offset
                                                  * @param _data stake limit state structure instance
                                                  */
                                                  function setStorageStakeLimitStruct(bytes32 _position, StakeLimitState.Data memory _data) internal {
                                                      _position.setStorageUint256(
                                                          uint256(_data.prevStakeBlockNumber) << PREV_STAKE_BLOCK_NUMBER_OFFSET
                                                              | uint256(_data.prevStakeLimit) << PREV_STAKE_LIMIT_OFFSET
                                                              | uint256(_data.maxStakeLimitGrowthBlocks) << MAX_STAKE_LIMIT_GROWTH_BLOCKS_OFFSET
                                                              | uint256(_data.maxStakeLimit) << MAX_STAKE_LIMIT_OFFSET
                                                      );
                                                  }
                                              }
                                              /**
                                              * @notice Interface library with helper functions to deal with stake limit struct in a more high-level approach.
                                              */
                                              library StakeLimitUtils {
                                                  /**
                                                  * @notice Calculate stake limit for the current block.
                                                  * @dev using `_constGasMin` to make gas consumption independent of the current block number
                                                  */
                                                  function calculateCurrentStakeLimit(StakeLimitState.Data memory _data) internal view returns(uint256 limit) {
                                                      uint256 stakeLimitIncPerBlock;
                                                      if (_data.maxStakeLimitGrowthBlocks != 0) {
                                                          stakeLimitIncPerBlock = _data.maxStakeLimit / _data.maxStakeLimitGrowthBlocks;
                                                      }
                                                      uint256 blocksPassed = block.number - _data.prevStakeBlockNumber;
                                                      uint256 projectedLimit = _data.prevStakeLimit + blocksPassed * stakeLimitIncPerBlock;
                                                      limit = _constGasMin(
                                                          projectedLimit,
                                                          _data.maxStakeLimit
                                                      );
                                                  }
                                                  /**
                                                  * @notice check if staking is on pause
                                                  */
                                                  function isStakingPaused(StakeLimitState.Data memory _data) internal pure returns(bool) {
                                                      return _data.prevStakeBlockNumber == 0;
                                                  }
                                                  /**
                                                  * @notice check if staking limit is set (otherwise staking is unlimited)
                                                  */
                                                  function isStakingLimitSet(StakeLimitState.Data memory _data) internal pure returns(bool) {
                                                      return _data.maxStakeLimit != 0;
                                                  }
                                                  /**
                                                  * @notice update stake limit repr with the desired limits
                                                  * @dev input `_data` param is mutated and the func returns effectively the same pointer
                                                  * @param _data stake limit state struct
                                                  * @param _maxStakeLimit stake limit max value
                                                  * @param _stakeLimitIncreasePerBlock stake limit increase (restoration) per block
                                                  */
                                                  function setStakingLimit(
                                                      StakeLimitState.Data memory _data,
                                                      uint256 _maxStakeLimit,
                                                      uint256 _stakeLimitIncreasePerBlock
                                                  ) internal view returns (StakeLimitState.Data memory) {
                                                      require(_maxStakeLimit != 0, "ZERO_MAX_STAKE_LIMIT");
                                                      require(_maxStakeLimit <= uint96(-1), "TOO_LARGE_MAX_STAKE_LIMIT");
                                                      require(_maxStakeLimit >= _stakeLimitIncreasePerBlock, "TOO_LARGE_LIMIT_INCREASE");
                                                      require(
                                                          (_stakeLimitIncreasePerBlock == 0)
                                                          || (_maxStakeLimit / _stakeLimitIncreasePerBlock <= uint32(-1)),
                                                          "TOO_SMALL_LIMIT_INCREASE"
                                                      );
                                                      // reset prev stake limit to the new max stake limit if
                                                      if (
                                                          // staking was paused or
                                                          _data.prevStakeBlockNumber == 0 ||
                                                          // staking was unlimited or
                                                          _data.maxStakeLimit == 0 ||
                                                          // new maximum limit value is lower than the value obtained on the previous stake submit
                                                          _maxStakeLimit < _data.prevStakeLimit
                                                      ) {
                                                          _data.prevStakeLimit = uint96(_maxStakeLimit);
                                                      }
                                                      _data.maxStakeLimitGrowthBlocks =
                                                          _stakeLimitIncreasePerBlock != 0 ? uint32(_maxStakeLimit / _stakeLimitIncreasePerBlock) : 0;
                                                      _data.maxStakeLimit = uint96(_maxStakeLimit);
                                                      if (_data.prevStakeBlockNumber != 0) {
                                                          _data.prevStakeBlockNumber = uint32(block.number);
                                                      }
                                                      return _data;
                                                  }
                                                  /**
                                                  * @notice update stake limit repr to remove the limit
                                                  * @dev input `_data` param is mutated and the func returns effectively the same pointer
                                                  * @param _data stake limit state struct
                                                  */
                                                  function removeStakingLimit(
                                                      StakeLimitState.Data memory _data
                                                  ) internal pure returns (StakeLimitState.Data memory) {
                                                      _data.maxStakeLimit = 0;
                                                      return _data;
                                                  }
                                                  /**
                                                  * @notice update stake limit repr after submitting user's eth
                                                  * @dev input `_data` param is mutated and the func returns effectively the same pointer
                                                  * @param _data stake limit state struct
                                                  * @param _newPrevStakeLimit new value for the `prevStakeLimit` field
                                                  */
                                                  function updatePrevStakeLimit(
                                                      StakeLimitState.Data memory _data,
                                                      uint256 _newPrevStakeLimit
                                                  ) internal view returns (StakeLimitState.Data memory) {
                                                      assert(_newPrevStakeLimit <= uint96(-1));
                                                      assert(_data.prevStakeBlockNumber != 0);
                                                      _data.prevStakeLimit = uint96(_newPrevStakeLimit);
                                                      _data.prevStakeBlockNumber = uint32(block.number);
                                                      return _data;
                                                  }
                                                  /**
                                                  * @notice set stake limit pause state (on or off)
                                                  * @dev input `_data` param is mutated and the func returns effectively the same pointer
                                                  * @param _data stake limit state struct
                                                  * @param _isPaused pause state flag
                                                  */
                                                  function setStakeLimitPauseState(
                                                      StakeLimitState.Data memory _data,
                                                      bool _isPaused
                                                  ) internal view returns (StakeLimitState.Data memory) {
                                                      _data.prevStakeBlockNumber = uint32(_isPaused ? 0 : block.number);
                                                      return _data;
                                                  }
                                                  /**
                                                   * @notice find a minimum of two numbers with a constant gas consumption
                                                   * @dev doesn't use branching logic inside
                                                   * @param _lhs left hand side value
                                                   * @param _rhs right hand side value
                                                   */
                                                  function _constGasMin(uint256 _lhs, uint256 _rhs) internal pure returns (uint256 min) {
                                                      uint256 lhsIsLess;
                                                      assembly {
                                                          lhsIsLess := lt(_lhs, _rhs) // lhsIsLess = (_lhs < _rhs) ? 1 : 0
                                                      }
                                                      min = (_lhs * lhsIsLess) + (_rhs * (1 - lhsIsLess));
                                                  }
                                              }
                                              // SPDX-FileCopyrightText: 2023 Lido <[email protected]>
                                              // SPDX-License-Identifier: GPL-3.0
                                              /* See contracts/COMPILERS.md */
                                              pragma solidity 0.4.24;
                                              import "@aragon/os/contracts/apps/AragonApp.sol";
                                              import "@aragon/os/contracts/lib/math/SafeMath.sol";
                                              import "../common/interfaces/ILidoLocator.sol";
                                              import "../common/interfaces/IBurner.sol";
                                              import "./lib/StakeLimitUtils.sol";
                                              import "../common/lib/Math256.sol";
                                              import "./StETHPermit.sol";
                                              import "./utils/Versioned.sol";
                                              interface IPostTokenRebaseReceiver {
                                                  function handlePostTokenRebase(
                                                      uint256 _reportTimestamp,
                                                      uint256 _timeElapsed,
                                                      uint256 _preTotalShares,
                                                      uint256 _preTotalEther,
                                                      uint256 _postTotalShares,
                                                      uint256 _postTotalEther,
                                                      uint256 _sharesMintedAsFees
                                                  ) external;
                                              }
                                              interface IOracleReportSanityChecker {
                                                  function checkAccountingOracleReport(
                                                      uint256 _timeElapsed,
                                                      uint256 _preCLBalance,
                                                      uint256 _postCLBalance,
                                                      uint256 _withdrawalVaultBalance,
                                                      uint256 _elRewardsVaultBalance,
                                                      uint256 _sharesRequestedToBurn,
                                                      uint256 _preCLValidators,
                                                      uint256 _postCLValidators
                                                  ) external view;
                                                  function smoothenTokenRebase(
                                                      uint256 _preTotalPooledEther,
                                                      uint256 _preTotalShares,
                                                      uint256 _preCLBalance,
                                                      uint256 _postCLBalance,
                                                      uint256 _withdrawalVaultBalance,
                                                      uint256 _elRewardsVaultBalance,
                                                      uint256 _sharesRequestedToBurn,
                                                      uint256 _etherToLockForWithdrawals,
                                                      uint256 _newSharesToBurnForWithdrawals
                                                  ) external view returns (
                                                      uint256 withdrawals,
                                                      uint256 elRewards,
                                                      uint256 simulatedSharesToBurn,
                                                      uint256 sharesToBurn
                                                  );
                                                  function checkWithdrawalQueueOracleReport(
                                                      uint256 _lastFinalizableRequestId,
                                                      uint256 _reportTimestamp
                                                  ) external view;
                                                  function checkSimulatedShareRate(
                                                      uint256 _postTotalPooledEther,
                                                      uint256 _postTotalShares,
                                                      uint256 _etherLockedOnWithdrawalQueue,
                                                      uint256 _sharesBurntDueToWithdrawals,
                                                      uint256 _simulatedShareRate
                                                  ) external view;
                                              }
                                              interface ILidoExecutionLayerRewardsVault {
                                                  function withdrawRewards(uint256 _maxAmount) external returns (uint256 amount);
                                              }
                                              interface IWithdrawalVault {
                                                  function withdrawWithdrawals(uint256 _amount) external;
                                              }
                                              interface IStakingRouter {
                                                  function deposit(
                                                      uint256 _depositsCount,
                                                      uint256 _stakingModuleId,
                                                      bytes _depositCalldata
                                                  ) external payable;
                                                  function getStakingRewardsDistribution()
                                                      external
                                                      view
                                                      returns (
                                                          address[] memory recipients,
                                                          uint256[] memory stakingModuleIds,
                                                          uint96[] memory stakingModuleFees,
                                                          uint96 totalFee,
                                                          uint256 precisionPoints
                                                      );
                                                  function getWithdrawalCredentials() external view returns (bytes32);
                                                  function reportRewardsMinted(uint256[] _stakingModuleIds, uint256[] _totalShares) external;
                                                  function getTotalFeeE4Precision() external view returns (uint16 totalFee);
                                                  function getStakingFeeAggregateDistributionE4Precision() external view returns (
                                                      uint16 modulesFee, uint16 treasuryFee
                                                  );
                                                  function getStakingModuleMaxDepositsCount(uint256 _stakingModuleId, uint256 _maxDepositsValue)
                                                      external
                                                      view
                                                      returns (uint256);
                                                  function TOTAL_BASIS_POINTS() external view returns (uint256);
                                              }
                                              interface IWithdrawalQueue {
                                                  function prefinalize(uint256[] _batches, uint256 _maxShareRate)
                                                      external
                                                      view
                                                      returns (uint256 ethToLock, uint256 sharesToBurn);
                                                  function finalize(uint256 _lastIdToFinalize, uint256 _maxShareRate) external payable;
                                                  function isPaused() external view returns (bool);
                                                  function unfinalizedStETH() external view returns (uint256);
                                                  function isBunkerModeActive() external view returns (bool);
                                              }
                                              /**
                                              * @title Liquid staking pool implementation
                                              *
                                              * Lido is an Ethereum liquid staking protocol solving the problem of frozen staked ether on Consensus Layer
                                              * being unavailable for transfers and DeFi on Execution Layer.
                                              *
                                              * Since balances of all token holders change when the amount of total pooled Ether
                                              * changes, this token cannot fully implement ERC20 standard: it only emits `Transfer`
                                              * events upon explicit transfer between holders. In contrast, when Lido oracle reports
                                              * rewards, no Transfer events are generated: doing so would require emitting an event
                                              * for each token holder and thus running an unbounded loop.
                                              *
                                              * ---
                                              * NB: Order of inheritance must preserve the structured storage layout of the previous versions.
                                              *
                                              * @dev Lido is derived from `StETHPermit` that has a structured storage:
                                              * SLOT 0: mapping (address => uint256) private shares (`StETH`)
                                              * SLOT 1: mapping (address => mapping (address => uint256)) private allowances (`StETH`)
                                              * SLOT 2: mapping(address => uint256) internal noncesByAddress (`StETHPermit`)
                                              *
                                              * `Versioned` and `AragonApp` both don't have the pre-allocated structured storage.
                                              */
                                              contract Lido is Versioned, StETHPermit, AragonApp {
                                                  using SafeMath for uint256;
                                                  using UnstructuredStorage for bytes32;
                                                  using StakeLimitUnstructuredStorage for bytes32;
                                                  using StakeLimitUtils for StakeLimitState.Data;
                                                  /// ACL
                                                  bytes32 public constant PAUSE_ROLE =
                                                      0x139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d; // keccak256("PAUSE_ROLE");
                                                  bytes32 public constant RESUME_ROLE =
                                                      0x2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7; // keccak256("RESUME_ROLE");
                                                  bytes32 public constant STAKING_PAUSE_ROLE =
                                                      0x84ea57490227bc2be925c684e2a367071d69890b629590198f4125a018eb1de8; // keccak256("STAKING_PAUSE_ROLE")
                                                  bytes32 public constant STAKING_CONTROL_ROLE =
                                                      0xa42eee1333c0758ba72be38e728b6dadb32ea767de5b4ddbaea1dae85b1b051f; // keccak256("STAKING_CONTROL_ROLE")
                                                  bytes32 public constant UNSAFE_CHANGE_DEPOSITED_VALIDATORS_ROLE =
                                                      0xe6dc5d79630c61871e99d341ad72c5a052bed2fc8c79e5a4480a7cd31117576c; // keccak256("UNSAFE_CHANGE_DEPOSITED_VALIDATORS_ROLE")
                                                  uint256 private constant DEPOSIT_SIZE = 32 ether;
                                                  /// @dev storage slot position for the Lido protocol contracts locator
                                                  bytes32 internal constant LIDO_LOCATOR_POSITION =
                                                      0x9ef78dff90f100ea94042bd00ccb978430524befc391d3e510b5f55ff3166df7; // keccak256("lido.Lido.lidoLocator")
                                                  /// @dev storage slot position of the staking rate limit structure
                                                  bytes32 internal constant STAKING_STATE_POSITION =
                                                      0xa3678de4a579be090bed1177e0a24f77cc29d181ac22fd7688aca344d8938015; // keccak256("lido.Lido.stakeLimit");
                                                  /// @dev amount of Ether (on the current Ethereum side) buffered on this smart contract balance
                                                  bytes32 internal constant BUFFERED_ETHER_POSITION =
                                                      0xed310af23f61f96daefbcd140b306c0bdbf8c178398299741687b90e794772b0; // keccak256("lido.Lido.bufferedEther");
                                                  /// @dev number of deposited validators (incrementing counter of deposit operations).
                                                  bytes32 internal constant DEPOSITED_VALIDATORS_POSITION =
                                                      0xe6e35175eb53fc006520a2a9c3e9711a7c00de6ff2c32dd31df8c5a24cac1b5c; // keccak256("lido.Lido.depositedValidators");
                                                  /// @dev total amount of ether on Consensus Layer (sum of all the balances of Lido validators)
                                                  // "beacon" in the `keccak256()` parameter is staying here for compatibility reason
                                                  bytes32 internal constant CL_BALANCE_POSITION =
                                                      0xa66d35f054e68143c18f32c990ed5cb972bb68a68f500cd2dd3a16bbf3686483; // keccak256("lido.Lido.beaconBalance");
                                                  /// @dev number of Lido's validators available in the Consensus Layer state
                                                  // "beacon" in the `keccak256()` parameter is staying here for compatibility reason
                                                  bytes32 internal constant CL_VALIDATORS_POSITION =
                                                      0x9f70001d82b6ef54e9d3725b46581c3eb9ee3aa02b941b6aa54d678a9ca35b10; // keccak256("lido.Lido.beaconValidators");
                                                  /// @dev Just a counter of total amount of execution layer rewards received by Lido contract. Not used in the logic.
                                                  bytes32 internal constant TOTAL_EL_REWARDS_COLLECTED_POSITION =
                                                      0xafe016039542d12eec0183bb0b1ffc2ca45b027126a494672fba4154ee77facb; // keccak256("lido.Lido.totalELRewardsCollected");
                                                  // Staking was paused (don't accept user's ether submits)
                                                  event StakingPaused();
                                                  // Staking was resumed (accept user's ether submits)
                                                  event StakingResumed();
                                                  // Staking limit was set (rate limits user's submits)
                                                  event StakingLimitSet(uint256 maxStakeLimit, uint256 stakeLimitIncreasePerBlock);
                                                  // Staking limit was removed
                                                  event StakingLimitRemoved();
                                                  // Emits when validators number delivered by the oracle
                                                  event CLValidatorsUpdated(
                                                      uint256 indexed reportTimestamp,
                                                      uint256 preCLValidators,
                                                      uint256 postCLValidators
                                                  );
                                                  // Emits when var at `DEPOSITED_VALIDATORS_POSITION` changed
                                                  event DepositedValidatorsChanged(
                                                      uint256 depositedValidators
                                                  );
                                                  // Emits when oracle accounting report processed
                                                  event ETHDistributed(
                                                      uint256 indexed reportTimestamp,
                                                      uint256 preCLBalance,
                                                      uint256 postCLBalance,
                                                      uint256 withdrawalsWithdrawn,
                                                      uint256 executionLayerRewardsWithdrawn,
                                                      uint256 postBufferedEther
                                                  );
                                                  // Emits when token rebased (total supply and/or total shares were changed)
                                                  event TokenRebased(
                                                      uint256 indexed reportTimestamp,
                                                      uint256 timeElapsed,
                                                      uint256 preTotalShares,
                                                      uint256 preTotalEther,
                                                      uint256 postTotalShares,
                                                      uint256 postTotalEther,
                                                      uint256 sharesMintedAsFees
                                                  );
                                                  // Lido locator set
                                                  event LidoLocatorSet(address lidoLocator);
                                                  // The amount of ETH withdrawn from LidoExecutionLayerRewardsVault to Lido
                                                  event ELRewardsReceived(uint256 amount);
                                                  // The amount of ETH withdrawn from WithdrawalVault to Lido
                                                  event WithdrawalsReceived(uint256 amount);
                                                  // Records a deposit made by a user
                                                  event Submitted(address indexed sender, uint256 amount, address referral);
                                                  // The `amount` of ether was sent to the deposit_contract.deposit function
                                                  event Unbuffered(uint256 amount);
                                                  /**
                                                  * @dev As AragonApp, Lido contract must be initialized with following variables:
                                                  *      NB: by default, staking and the whole Lido pool are in paused state
                                                  *
                                                  * The contract's balance must be non-zero to allow initial holder bootstrap.
                                                  *
                                                  * @param _lidoLocator lido locator contract
                                                  * @param _eip712StETH eip712 helper contract for StETH
                                                  */
                                                  function initialize(address _lidoLocator, address _eip712StETH)
                                                      public
                                                      payable
                                                      onlyInit
                                                  {
                                                      _bootstrapInitialHolder();
                                                      _initialize_v2(_lidoLocator, _eip712StETH);
                                                      initialized();
                                                  }
                                                  /**
                                                   * initializer for the Lido version "2"
                                                   */
                                                  function _initialize_v2(address _lidoLocator, address _eip712StETH) internal {
                                                      _setContractVersion(2);
                                                      LIDO_LOCATOR_POSITION.setStorageAddress(_lidoLocator);
                                                      _initializeEIP712StETH(_eip712StETH);
                                                      // set infinite allowance for burner from withdrawal queue
                                                      // to burn finalized requests' shares
                                                      _approve(
                                                          ILidoLocator(_lidoLocator).withdrawalQueue(),
                                                          ILidoLocator(_lidoLocator).burner(),
                                                          INFINITE_ALLOWANCE
                                                      );
                                                      emit LidoLocatorSet(_lidoLocator);
                                                  }
                                                  /**
                                                   * @notice A function to finalize upgrade to v2 (from v1). Can be called only once
                                                   * @dev Value "1" in CONTRACT_VERSION_POSITION is skipped due to change in numbering
                                                   *
                                                   * The initial protocol token holder must exist.
                                                   *
                                                   * For more details see https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-10.md
                                                   */
                                                  function finalizeUpgrade_v2(address _lidoLocator, address _eip712StETH) external {
                                                      _checkContractVersion(0);
                                                      require(hasInitialized(), "NOT_INITIALIZED");
                                                      require(_lidoLocator != address(0), "LIDO_LOCATOR_ZERO_ADDRESS");
                                                      require(_eip712StETH != address(0), "EIP712_STETH_ZERO_ADDRESS");
                                                      require(_sharesOf(INITIAL_TOKEN_HOLDER) != 0, "INITIAL_HOLDER_EXISTS");
                                                      _initialize_v2(_lidoLocator, _eip712StETH);
                                                  }
                                                  /**
                                                   * @notice Stops accepting new Ether to the protocol
                                                   *
                                                   * @dev While accepting new Ether is stopped, calls to the `submit` function,
                                                   * as well as to the default payable function, will revert.
                                                   *
                                                   * Emits `StakingPaused` event.
                                                   */
                                                  function pauseStaking() external {
                                                      _auth(STAKING_PAUSE_ROLE);
                                                      _pauseStaking();
                                                  }
                                                  /**
                                                   * @notice Resumes accepting new Ether to the protocol (if `pauseStaking` was called previously)
                                                   * NB: Staking could be rate-limited by imposing a limit on the stake amount
                                                   * at each moment in time, see `setStakingLimit()` and `removeStakingLimit()`
                                                   *
                                                   * @dev Preserves staking limit if it was set previously
                                                   *
                                                   * Emits `StakingResumed` event
                                                   */
                                                  function resumeStaking() external {
                                                      _auth(STAKING_CONTROL_ROLE);
                                                      require(hasInitialized(), "NOT_INITIALIZED");
                                                      _resumeStaking();
                                                  }
                                                  /**
                                                   * @notice Sets the staking rate limit
                                                   *
                                                   * ▲ Stake limit
                                                   * │.....  .....   ........ ...            ....     ... Stake limit = max
                                                   * │      .       .        .   .   .      .    . . .
                                                   * │     .       .              . .  . . .      . .
                                                   * │            .                .  . . .
                                                   * │──────────────────────────────────────────────────> Time
                                                   * │     ^      ^          ^   ^^^  ^ ^ ^     ^^^ ^     Stake events
                                                   *
                                                   * @dev Reverts if:
                                                   * - `_maxStakeLimit` == 0
                                                   * - `_maxStakeLimit` >= 2^96
                                                   * - `_maxStakeLimit` < `_stakeLimitIncreasePerBlock`
                                                   * - `_maxStakeLimit` / `_stakeLimitIncreasePerBlock` >= 2^32 (only if `_stakeLimitIncreasePerBlock` != 0)
                                                   *
                                                   * Emits `StakingLimitSet` event
                                                   *
                                                   * @param _maxStakeLimit max stake limit value
                                                   * @param _stakeLimitIncreasePerBlock stake limit increase per single block
                                                   */
                                                  function setStakingLimit(uint256 _maxStakeLimit, uint256 _stakeLimitIncreasePerBlock) external {
                                                      _auth(STAKING_CONTROL_ROLE);
                                                      STAKING_STATE_POSITION.setStorageStakeLimitStruct(
                                                          STAKING_STATE_POSITION.getStorageStakeLimitStruct().setStakingLimit(_maxStakeLimit, _stakeLimitIncreasePerBlock)
                                                      );
                                                      emit StakingLimitSet(_maxStakeLimit, _stakeLimitIncreasePerBlock);
                                                  }
                                                  /**
                                                   * @notice Removes the staking rate limit
                                                   *
                                                   * Emits `StakingLimitRemoved` event
                                                   */
                                                  function removeStakingLimit() external {
                                                      _auth(STAKING_CONTROL_ROLE);
                                                      STAKING_STATE_POSITION.setStorageStakeLimitStruct(STAKING_STATE_POSITION.getStorageStakeLimitStruct().removeStakingLimit());
                                                      emit StakingLimitRemoved();
                                                  }
                                                  /**
                                                   * @notice Check staking state: whether it's paused or not
                                                   */
                                                  function isStakingPaused() external view returns (bool) {
                                                      return STAKING_STATE_POSITION.getStorageStakeLimitStruct().isStakingPaused();
                                                  }
                                                  /**
                                                   * @notice Returns how much Ether can be staked in the current block
                                                   * @dev Special return values:
                                                   * - 2^256 - 1 if staking is unlimited;
                                                   * - 0 if staking is paused or if limit is exhausted.
                                                   */
                                                  function getCurrentStakeLimit() external view returns (uint256) {
                                                      return _getCurrentStakeLimit(STAKING_STATE_POSITION.getStorageStakeLimitStruct());
                                                  }
                                                  /**
                                                   * @notice Returns full info about current stake limit params and state
                                                   * @dev Might be used for the advanced integration requests.
                                                   * @return isStakingPaused staking pause state (equivalent to return of isStakingPaused())
                                                   * @return isStakingLimitSet whether the stake limit is set
                                                   * @return currentStakeLimit current stake limit (equivalent to return of getCurrentStakeLimit())
                                                   * @return maxStakeLimit max stake limit
                                                   * @return maxStakeLimitGrowthBlocks blocks needed to restore max stake limit from the fully exhausted state
                                                   * @return prevStakeLimit previously reached stake limit
                                                   * @return prevStakeBlockNumber previously seen block number
                                                   */
                                                  function getStakeLimitFullInfo()
                                                      external
                                                      view
                                                      returns (
                                                          bool isStakingPaused,
                                                          bool isStakingLimitSet,
                                                          uint256 currentStakeLimit,
                                                          uint256 maxStakeLimit,
                                                          uint256 maxStakeLimitGrowthBlocks,
                                                          uint256 prevStakeLimit,
                                                          uint256 prevStakeBlockNumber
                                                      )
                                                  {
                                                      StakeLimitState.Data memory stakeLimitData = STAKING_STATE_POSITION.getStorageStakeLimitStruct();
                                                      isStakingPaused = stakeLimitData.isStakingPaused();
                                                      isStakingLimitSet = stakeLimitData.isStakingLimitSet();
                                                      currentStakeLimit = _getCurrentStakeLimit(stakeLimitData);
                                                      maxStakeLimit = stakeLimitData.maxStakeLimit;
                                                      maxStakeLimitGrowthBlocks = stakeLimitData.maxStakeLimitGrowthBlocks;
                                                      prevStakeLimit = stakeLimitData.prevStakeLimit;
                                                      prevStakeBlockNumber = stakeLimitData.prevStakeBlockNumber;
                                                  }
                                                  /**
                                                  * @notice Send funds to the pool
                                                  * @dev Users are able to submit their funds by transacting to the fallback function.
                                                  * Unlike vanilla Ethereum Deposit contract, accepting only 32-Ether transactions, Lido
                                                  * accepts payments of any size. Submitted Ethers are stored in Buffer until someone calls
                                                  * deposit() and pushes them to the Ethereum Deposit contract.
                                                  */
                                                  // solhint-disable-next-line no-complex-fallback
                                                  function() external payable {
                                                      // protection against accidental submissions by calling non-existent function
                                                      require(msg.data.length == 0, "NON_EMPTY_DATA");
                                                      _submit(0);
                                                  }
                                                  /**
                                                   * @notice Send funds to the pool with optional _referral parameter
                                                   * @dev This function is alternative way to submit funds. Supports optional referral address.
                                                   * @return Amount of StETH shares generated
                                                   */
                                                  function submit(address _referral) external payable returns (uint256) {
                                                      return _submit(_referral);
                                                  }
                                                  /**
                                                   * @notice A payable function for execution layer rewards. Can be called only by `ExecutionLayerRewardsVault`
                                                   * @dev We need a dedicated function because funds received by the default payable function
                                                   * are treated as a user deposit
                                                   */
                                                  function receiveELRewards() external payable {
                                                      require(msg.sender == getLidoLocator().elRewardsVault());
                                                      TOTAL_EL_REWARDS_COLLECTED_POSITION.setStorageUint256(getTotalELRewardsCollected().add(msg.value));
                                                      emit ELRewardsReceived(msg.value);
                                                  }
                                                  /**
                                                  * @notice A payable function for withdrawals acquisition. Can be called only by `WithdrawalVault`
                                                  * @dev We need a dedicated function because funds received by the default payable function
                                                  * are treated as a user deposit
                                                  */
                                                  function receiveWithdrawals() external payable {
                                                      require(msg.sender == getLidoLocator().withdrawalVault());
                                                      emit WithdrawalsReceived(msg.value);
                                                  }
                                                  /**
                                                   * @notice Stop pool routine operations
                                                   */
                                                  function stop() external {
                                                      _auth(PAUSE_ROLE);
                                                      _stop();
                                                      _pauseStaking();
                                                  }
                                                  /**
                                                   * @notice Resume pool routine operations
                                                   * @dev Staking is resumed after this call using the previously set limits (if any)
                                                   */
                                                  function resume() external {
                                                      _auth(RESUME_ROLE);
                                                      _resume();
                                                      _resumeStaking();
                                                  }
                                                  /**
                                                   * The structure is used to aggregate the `handleOracleReport` provided data.
                                                   * @dev Using the in-memory structure addresses `stack too deep` issues.
                                                   */
                                                  struct OracleReportedData {
                                                      // Oracle timings
                                                      uint256 reportTimestamp;
                                                      uint256 timeElapsed;
                                                      // CL values
                                                      uint256 clValidators;
                                                      uint256 postCLBalance;
                                                      // EL values
                                                      uint256 withdrawalVaultBalance;
                                                      uint256 elRewardsVaultBalance;
                                                      uint256 sharesRequestedToBurn;
                                                      // Decision about withdrawals processing
                                                      uint256[] withdrawalFinalizationBatches;
                                                      uint256 simulatedShareRate;
                                                  }
                                                  /**
                                                   * The structure is used to preload the contract using `getLidoLocator()` via single call
                                                   */
                                                  struct OracleReportContracts {
                                                      address accountingOracle;
                                                      address elRewardsVault;
                                                      address oracleReportSanityChecker;
                                                      address burner;
                                                      address withdrawalQueue;
                                                      address withdrawalVault;
                                                      address postTokenRebaseReceiver;
                                                  }
                                                  /**
                                                  * @notice Updates accounting stats, collects EL rewards and distributes collected rewards
                                                  *         if beacon balance increased, performs withdrawal requests finalization
                                                  * @dev periodically called by the AccountingOracle contract
                                                  *
                                                  * @param _reportTimestamp the moment of the oracle report calculation
                                                  * @param _timeElapsed seconds elapsed since the previous report calculation
                                                  * @param _clValidators number of Lido validators on Consensus Layer
                                                  * @param _clBalance sum of all Lido validators' balances on Consensus Layer
                                                  * @param _withdrawalVaultBalance withdrawal vault balance on Execution Layer at `_reportTimestamp`
                                                  * @param _elRewardsVaultBalance elRewards vault balance on Execution Layer at `_reportTimestamp`
                                                  * @param _sharesRequestedToBurn shares requested to burn through Burner at `_reportTimestamp`
                                                  * @param _withdrawalFinalizationBatches the ascendingly-sorted array of withdrawal request IDs obtained by calling
                                                  * WithdrawalQueue.calculateFinalizationBatches. Empty array means that no withdrawal requests should be finalized
                                                  * @param _simulatedShareRate share rate that was simulated by oracle when the report data created (1e27 precision)
                                                  *
                                                  * NB: `_simulatedShareRate` should be calculated off-chain by calling the method with `eth_call` JSON-RPC API
                                                  * while passing empty `_withdrawalFinalizationBatches` and `_simulatedShareRate` == 0, plugging the returned values
                                                  * to the following formula: `_simulatedShareRate = (postTotalPooledEther * 1e27) / postTotalShares`
                                                  *
                                                  * @return postRebaseAmounts[0]: `postTotalPooledEther` amount of ether in the protocol after report
                                                  * @return postRebaseAmounts[1]: `postTotalShares` amount of shares in the protocol after report
                                                  * @return postRebaseAmounts[2]: `withdrawals` withdrawn from the withdrawals vault
                                                  * @return postRebaseAmounts[3]: `elRewards` withdrawn from the execution layer rewards vault
                                                  */
                                                  function handleOracleReport(
                                                      // Oracle timings
                                                      uint256 _reportTimestamp,
                                                      uint256 _timeElapsed,
                                                      // CL values
                                                      uint256 _clValidators,
                                                      uint256 _clBalance,
                                                      // EL values
                                                      uint256 _withdrawalVaultBalance,
                                                      uint256 _elRewardsVaultBalance,
                                                      uint256 _sharesRequestedToBurn,
                                                      // Decision about withdrawals processing
                                                      uint256[] _withdrawalFinalizationBatches,
                                                      uint256 _simulatedShareRate
                                                  ) external returns (uint256[4] postRebaseAmounts) {
                                                      _whenNotStopped();
                                                      return _handleOracleReport(
                                                          OracleReportedData(
                                                              _reportTimestamp,
                                                              _timeElapsed,
                                                              _clValidators,
                                                              _clBalance,
                                                              _withdrawalVaultBalance,
                                                              _elRewardsVaultBalance,
                                                              _sharesRequestedToBurn,
                                                              _withdrawalFinalizationBatches,
                                                              _simulatedShareRate
                                                          )
                                                      );
                                                  }
                                                  /**
                                                   * @notice Unsafely change deposited validators
                                                   *
                                                   * The method unsafely changes deposited validator counter.
                                                   * Can be required when onboarding external validators to Lido
                                                   * (i.e., had deposited before and rotated their type-0x00 withdrawal credentials to Lido)
                                                   *
                                                   * @param _newDepositedValidators new value
                                                   */
                                                  function unsafeChangeDepositedValidators(uint256 _newDepositedValidators) external {
                                                      _auth(UNSAFE_CHANGE_DEPOSITED_VALIDATORS_ROLE);
                                                      DEPOSITED_VALIDATORS_POSITION.setStorageUint256(_newDepositedValidators);
                                                      emit DepositedValidatorsChanged(_newDepositedValidators);
                                                  }
                                                  /**
                                                   * @notice Overrides default AragonApp behaviour to disallow recovery.
                                                   */
                                                  function transferToVault(address /* _token */) external {
                                                      revert("NOT_SUPPORTED");
                                                  }
                                                  /**
                                                  * @notice Get the amount of Ether temporary buffered on this contract balance
                                                  * @dev Buffered balance is kept on the contract from the moment the funds are received from user
                                                  * until the moment they are actually sent to the official Deposit contract.
                                                  * @return amount of buffered funds in wei
                                                  */
                                                  function getBufferedEther() external view returns (uint256) {
                                                      return _getBufferedEther();
                                                  }
                                                  /**
                                                   * @notice Get total amount of execution layer rewards collected to Lido contract
                                                   * @dev Ether got through LidoExecutionLayerRewardsVault is kept on this contract's balance the same way
                                                   * as other buffered Ether is kept (until it gets deposited)
                                                   * @return amount of funds received as execution layer rewards in wei
                                                   */
                                                  function getTotalELRewardsCollected() public view returns (uint256) {
                                                      return TOTAL_EL_REWARDS_COLLECTED_POSITION.getStorageUint256();
                                                  }
                                                  /**
                                                   * @notice Gets authorized oracle address
                                                   * @return address of oracle contract
                                                   */
                                                  function getLidoLocator() public view returns (ILidoLocator) {
                                                      return ILidoLocator(LIDO_LOCATOR_POSITION.getStorageAddress());
                                                  }
                                                  /**
                                                  * @notice Returns the key values related to Consensus Layer side of the contract. It historically contains beacon
                                                  * @return depositedValidators - number of deposited validators from Lido contract side
                                                  * @return beaconValidators - number of Lido validators visible on Consensus Layer, reported by oracle
                                                  * @return beaconBalance - total amount of ether on the Consensus Layer side (sum of all the balances of Lido validators)
                                                  *
                                                  * @dev `beacon` in naming still here for historical reasons
                                                  */
                                                  function getBeaconStat() external view returns (uint256 depositedValidators, uint256 beaconValidators, uint256 beaconBalance) {
                                                      depositedValidators = DEPOSITED_VALIDATORS_POSITION.getStorageUint256();
                                                      beaconValidators = CL_VALIDATORS_POSITION.getStorageUint256();
                                                      beaconBalance = CL_BALANCE_POSITION.getStorageUint256();
                                                  }
                                                  /**
                                                   * @dev Check that Lido allows depositing buffered ether to the consensus layer
                                                   * Depends on the bunker state and protocol's pause state
                                                   */
                                                  function canDeposit() public view returns (bool) {
                                                      return !_withdrawalQueue().isBunkerModeActive() && !isStopped();
                                                  }
                                                  /**
                                                   * @dev Returns depositable ether amount.
                                                   * Takes into account unfinalized stETH required by WithdrawalQueue
                                                   */
                                                  function getDepositableEther() public view returns (uint256) {
                                                      uint256 bufferedEther = _getBufferedEther();
                                                      uint256 withdrawalReserve = _withdrawalQueue().unfinalizedStETH();
                                                      return bufferedEther > withdrawalReserve ? bufferedEther - withdrawalReserve : 0;
                                                  }
                                                  /**
                                                   * @dev Invokes a deposit call to the Staking Router contract and updates buffered counters
                                                   * @param _maxDepositsCount max deposits count
                                                   * @param _stakingModuleId id of the staking module to be deposited
                                                   * @param _depositCalldata module calldata
                                                   */
                                                  function deposit(uint256 _maxDepositsCount, uint256 _stakingModuleId, bytes _depositCalldata) external {
                                                      ILidoLocator locator = getLidoLocator();
                                                      require(msg.sender == locator.depositSecurityModule(), "APP_AUTH_DSM_FAILED");
                                                      require(canDeposit(), "CAN_NOT_DEPOSIT");
                                                      IStakingRouter stakingRouter = _stakingRouter();
                                                      uint256 depositsCount = Math256.min(
                                                          _maxDepositsCount,
                                                          stakingRouter.getStakingModuleMaxDepositsCount(_stakingModuleId, getDepositableEther())
                                                      );
                                                      uint256 depositsValue;
                                                      if (depositsCount > 0) {
                                                          depositsValue = depositsCount.mul(DEPOSIT_SIZE);
                                                          /// @dev firstly update the local state of the contract to prevent a reentrancy attack,
                                                          ///     even if the StakingRouter is a trusted contract.
                                                          BUFFERED_ETHER_POSITION.setStorageUint256(_getBufferedEther().sub(depositsValue));
                                                          emit Unbuffered(depositsValue);
                                                          uint256 newDepositedValidators = DEPOSITED_VALIDATORS_POSITION.getStorageUint256().add(depositsCount);
                                                          DEPOSITED_VALIDATORS_POSITION.setStorageUint256(newDepositedValidators);
                                                          emit DepositedValidatorsChanged(newDepositedValidators);
                                                      }
                                                      /// @dev transfer ether to StakingRouter and make a deposit at the same time. All the ether
                                                      ///     sent to StakingRouter is counted as deposited. If StakingRouter can't deposit all
                                                      ///     passed ether it MUST revert the whole transaction (never happens in normal circumstances)
                                                      stakingRouter.deposit.value(depositsValue)(depositsCount, _stakingModuleId, _depositCalldata);
                                                  }
                                                  /// DEPRECATED PUBLIC METHODS
                                                  /**
                                                   * @notice Returns current withdrawal credentials of deposited validators
                                                   * @dev DEPRECATED: use StakingRouter.getWithdrawalCredentials() instead
                                                   */
                                                  function getWithdrawalCredentials() external view returns (bytes32) {
                                                      return _stakingRouter().getWithdrawalCredentials();
                                                  }
                                                  /**
                                                   * @notice Returns legacy oracle
                                                   * @dev DEPRECATED: the `AccountingOracle` superseded the old one
                                                   */
                                                  function getOracle() external view returns (address) {
                                                      return getLidoLocator().legacyOracle();
                                                  }
                                                  /**
                                                   * @notice Returns the treasury address
                                                   * @dev DEPRECATED: use LidoLocator.treasury()
                                                   */
                                                  function getTreasury() external view returns (address) {
                                                      return _treasury();
                                                  }
                                                  /**
                                                   * @notice Returns current staking rewards fee rate
                                                   * @dev DEPRECATED: Now fees information is stored in StakingRouter and
                                                   * with higher precision. Use StakingRouter.getStakingFeeAggregateDistribution() instead.
                                                   * @return totalFee total rewards fee in 1e4 precision (10000 is 100%). The value might be
                                                   * inaccurate because the actual value is truncated here to 1e4 precision.
                                                   */
                                                  function getFee() external view returns (uint16 totalFee) {
                                                      totalFee = _stakingRouter().getTotalFeeE4Precision();
                                                  }
                                                  /**
                                                   * @notice Returns current fee distribution, values relative to the total fee (getFee())
                                                   * @dev DEPRECATED: Now fees information is stored in StakingRouter and
                                                   * with higher precision. Use StakingRouter.getStakingFeeAggregateDistribution() instead.
                                                   * @return treasuryFeeBasisPoints return treasury fee in TOTAL_BASIS_POINTS (10000 is 100% fee) precision
                                                   * @return insuranceFeeBasisPoints always returns 0 because the capability to send fees to
                                                   * insurance from Lido contract is removed.
                                                   * @return operatorsFeeBasisPoints return total fee for all operators of all staking modules in
                                                   * TOTAL_BASIS_POINTS (10000 is 100% fee) precision.
                                                   * Previously returned total fee of all node operators of NodeOperatorsRegistry (Curated staking module now)
                                                   * The value might be inaccurate because the actual value is truncated here to 1e4 precision.
                                                   */
                                                  function getFeeDistribution()
                                                      external view
                                                      returns (
                                                          uint16 treasuryFeeBasisPoints,
                                                          uint16 insuranceFeeBasisPoints,
                                                          uint16 operatorsFeeBasisPoints
                                                      )
                                                  {
                                                      IStakingRouter stakingRouter = _stakingRouter();
                                                      uint256 totalBasisPoints = stakingRouter.TOTAL_BASIS_POINTS();
                                                      uint256 totalFee = stakingRouter.getTotalFeeE4Precision();
                                                      (uint256 treasuryFeeBasisPointsAbs, uint256 operatorsFeeBasisPointsAbs) = stakingRouter
                                                          .getStakingFeeAggregateDistributionE4Precision();
                                                      insuranceFeeBasisPoints = 0;  // explicitly set to zero
                                                      treasuryFeeBasisPoints = uint16((treasuryFeeBasisPointsAbs * totalBasisPoints) / totalFee);
                                                      operatorsFeeBasisPoints = uint16((operatorsFeeBasisPointsAbs * totalBasisPoints) / totalFee);
                                                  }
                                                  /*
                                                   * @dev updates Consensus Layer state snapshot according to the current report
                                                   *
                                                   * NB: conventions and assumptions
                                                   *
                                                   * `depositedValidators` are total amount of the **ever** deposited Lido validators
                                                   * `_postClValidators` are total amount of the **ever** appeared on the CL side Lido validators
                                                   *
                                                   * i.e., exited Lido validators persist in the state, just with a different status
                                                   */
                                                  function _processClStateUpdate(
                                                      uint256 _reportTimestamp,
                                                      uint256 _preClValidators,
                                                      uint256 _postClValidators,
                                                      uint256 _postClBalance
                                                  ) internal returns (uint256 preCLBalance) {
                                                      uint256 depositedValidators = DEPOSITED_VALIDATORS_POSITION.getStorageUint256();
                                                      require(_postClValidators <= depositedValidators, "REPORTED_MORE_DEPOSITED");
                                                      require(_postClValidators >= _preClValidators, "REPORTED_LESS_VALIDATORS");
                                                      if (_postClValidators > _preClValidators) {
                                                          CL_VALIDATORS_POSITION.setStorageUint256(_postClValidators);
                                                      }
                                                      uint256 appearedValidators = _postClValidators - _preClValidators;
                                                      preCLBalance = CL_BALANCE_POSITION.getStorageUint256();
                                                      // Take into account the balance of the newly appeared validators
                                                      preCLBalance = preCLBalance.add(appearedValidators.mul(DEPOSIT_SIZE));
                                                      // Save the current CL balance and validators to
                                                      // calculate rewards on the next push
                                                      CL_BALANCE_POSITION.setStorageUint256(_postClBalance);
                                                      emit CLValidatorsUpdated(_reportTimestamp, _preClValidators, _postClValidators);
                                                  }
                                                  /**
                                                   * @dev collect ETH from ELRewardsVault and WithdrawalVault, then send to WithdrawalQueue
                                                   */
                                                  function _collectRewardsAndProcessWithdrawals(
                                                      OracleReportContracts memory _contracts,
                                                      uint256 _withdrawalsToWithdraw,
                                                      uint256 _elRewardsToWithdraw,
                                                      uint256[] _withdrawalFinalizationBatches,
                                                      uint256 _simulatedShareRate,
                                                      uint256 _etherToLockOnWithdrawalQueue
                                                  ) internal {
                                                      // withdraw execution layer rewards and put them to the buffer
                                                      if (_elRewardsToWithdraw > 0) {
                                                          ILidoExecutionLayerRewardsVault(_contracts.elRewardsVault).withdrawRewards(_elRewardsToWithdraw);
                                                      }
                                                      // withdraw withdrawals and put them to the buffer
                                                      if (_withdrawalsToWithdraw > 0) {
                                                          IWithdrawalVault(_contracts.withdrawalVault).withdrawWithdrawals(_withdrawalsToWithdraw);
                                                      }
                                                      // finalize withdrawals (send ether, assign shares for burning)
                                                      if (_etherToLockOnWithdrawalQueue > 0) {
                                                          IWithdrawalQueue withdrawalQueue = IWithdrawalQueue(_contracts.withdrawalQueue);
                                                          withdrawalQueue.finalize.value(_etherToLockOnWithdrawalQueue)(
                                                              _withdrawalFinalizationBatches[_withdrawalFinalizationBatches.length - 1],
                                                              _simulatedShareRate
                                                          );
                                                      }
                                                      uint256 postBufferedEther = _getBufferedEther()
                                                          .add(_elRewardsToWithdraw) // Collected from ELVault
                                                          .add(_withdrawalsToWithdraw) // Collected from WithdrawalVault
                                                          .sub(_etherToLockOnWithdrawalQueue); // Sent to WithdrawalQueue
                                                      _setBufferedEther(postBufferedEther);
                                                  }
                                                  /**
                                                   * @dev return amount to lock on withdrawal queue and shares to burn
                                                   * depending on the finalization batch parameters
                                                   */
                                                  function _calculateWithdrawals(
                                                      OracleReportContracts memory _contracts,
                                                      OracleReportedData memory _reportedData
                                                  ) internal view returns (
                                                      uint256 etherToLock, uint256 sharesToBurn
                                                  ) {
                                                      IWithdrawalQueue withdrawalQueue = IWithdrawalQueue(_contracts.withdrawalQueue);
                                                      if (!withdrawalQueue.isPaused()) {
                                                          IOracleReportSanityChecker(_contracts.oracleReportSanityChecker).checkWithdrawalQueueOracleReport(
                                                              _reportedData.withdrawalFinalizationBatches[_reportedData.withdrawalFinalizationBatches.length - 1],
                                                              _reportedData.reportTimestamp
                                                          );
                                                          (etherToLock, sharesToBurn) = withdrawalQueue.prefinalize(
                                                              _reportedData.withdrawalFinalizationBatches,
                                                              _reportedData.simulatedShareRate
                                                          );
                                                      }
                                                  }
                                                  /**
                                                   * @dev calculate the amount of rewards and distribute it
                                                   */
                                                  function _processRewards(
                                                      OracleReportContext memory _reportContext,
                                                      uint256 _postCLBalance,
                                                      uint256 _withdrawnWithdrawals,
                                                      uint256 _withdrawnElRewards
                                                  ) internal returns (uint256 sharesMintedAsFees) {
                                                      uint256 postCLTotalBalance = _postCLBalance.add(_withdrawnWithdrawals);
                                                      // Don’t mint/distribute any protocol fee on the non-profitable Lido oracle report
                                                      // (when consensus layer balance delta is zero or negative).
                                                      // See LIP-12 for details:
                                                      // https://research.lido.fi/t/lip-12-on-chain-part-of-the-rewards-distribution-after-the-merge/1625
                                                      if (postCLTotalBalance > _reportContext.preCLBalance) {
                                                          uint256 consensusLayerRewards = postCLTotalBalance - _reportContext.preCLBalance;
                                                          sharesMintedAsFees = _distributeFee(
                                                              _reportContext.preTotalPooledEther,
                                                              _reportContext.preTotalShares,
                                                              consensusLayerRewards.add(_withdrawnElRewards)
                                                          );
                                                      }
                                                  }
                                                  /**
                                                   * @dev Process user deposit, mints liquid tokens and increase the pool buffer
                                                   * @param _referral address of referral.
                                                   * @return amount of StETH shares generated
                                                   */
                                                  function _submit(address _referral) internal returns (uint256) {
                                                      require(msg.value != 0, "ZERO_DEPOSIT");
                                                      StakeLimitState.Data memory stakeLimitData = STAKING_STATE_POSITION.getStorageStakeLimitStruct();
                                                      // There is an invariant that protocol pause also implies staking pause.
                                                      // Thus, no need to check protocol pause explicitly.
                                                      require(!stakeLimitData.isStakingPaused(), "STAKING_PAUSED");
                                                      if (stakeLimitData.isStakingLimitSet()) {
                                                          uint256 currentStakeLimit = stakeLimitData.calculateCurrentStakeLimit();
                                                          require(msg.value <= currentStakeLimit, "STAKE_LIMIT");
                                                          STAKING_STATE_POSITION.setStorageStakeLimitStruct(stakeLimitData.updatePrevStakeLimit(currentStakeLimit - msg.value));
                                                      }
                                                      uint256 sharesAmount = getSharesByPooledEth(msg.value);
                                                      _mintShares(msg.sender, sharesAmount);
                                                      _setBufferedEther(_getBufferedEther().add(msg.value));
                                                      emit Submitted(msg.sender, msg.value, _referral);
                                                      _emitTransferAfterMintingShares(msg.sender, sharesAmount);
                                                      return sharesAmount;
                                                  }
                                                  /**
                                                   * @dev Staking router rewards distribution.
                                                   *
                                                   * Corresponds to the return value of `IStakingRouter.newTotalPooledEtherForRewards()`
                                                   * Prevents `stack too deep` issue.
                                                   */
                                                  struct StakingRewardsDistribution {
                                                      address[] recipients;
                                                      uint256[] moduleIds;
                                                      uint96[] modulesFees;
                                                      uint96 totalFee;
                                                      uint256 precisionPoints;
                                                  }
                                                  /**
                                                   * @dev Get staking rewards distribution from staking router.
                                                   */
                                                  function _getStakingRewardsDistribution() internal view returns (
                                                      StakingRewardsDistribution memory ret,
                                                      IStakingRouter router
                                                  ) {
                                                      router = _stakingRouter();
                                                      (
                                                          ret.recipients,
                                                          ret.moduleIds,
                                                          ret.modulesFees,
                                                          ret.totalFee,
                                                          ret.precisionPoints
                                                      ) = router.getStakingRewardsDistribution();
                                                      require(ret.recipients.length == ret.modulesFees.length, "WRONG_RECIPIENTS_INPUT");
                                                      require(ret.moduleIds.length == ret.modulesFees.length, "WRONG_MODULE_IDS_INPUT");
                                                  }
                                                  /**
                                                   * @dev Distributes fee portion of the rewards by minting and distributing corresponding amount of liquid tokens.
                                                   * @param _preTotalPooledEther Total supply before report-induced changes applied
                                                   * @param _preTotalShares Total shares before report-induced changes applied
                                                   * @param _totalRewards Total rewards accrued both on the Execution Layer and the Consensus Layer sides in wei.
                                                   */
                                                  function _distributeFee(
                                                      uint256 _preTotalPooledEther,
                                                      uint256 _preTotalShares,
                                                      uint256 _totalRewards
                                                  ) internal returns (uint256 sharesMintedAsFees) {
                                                      // We need to take a defined percentage of the reported reward as a fee, and we do
                                                      // this by minting new token shares and assigning them to the fee recipients (see
                                                      // StETH docs for the explanation of the shares mechanics). The staking rewards fee
                                                      // is defined in basis points (1 basis point is equal to 0.01%, 10000 (TOTAL_BASIS_POINTS) is 100%).
                                                      //
                                                      // Since we are increasing totalPooledEther by _totalRewards (totalPooledEtherWithRewards),
                                                      // the combined cost of all holders' shares has became _totalRewards StETH tokens more,
                                                      // effectively splitting the reward between each token holder proportionally to their token share.
                                                      //
                                                      // Now we want to mint new shares to the fee recipient, so that the total cost of the
                                                      // newly-minted shares exactly corresponds to the fee taken:
                                                      //
                                                      // totalPooledEtherWithRewards = _preTotalPooledEther + _totalRewards
                                                      // shares2mint * newShareCost = (_totalRewards * totalFee) / PRECISION_POINTS
                                                      // newShareCost = totalPooledEtherWithRewards / (_preTotalShares + shares2mint)
                                                      //
                                                      // which follows to:
                                                      //
                                                      //                        _totalRewards * totalFee * _preTotalShares
                                                      // shares2mint = --------------------------------------------------------------
                                                      //                 (totalPooledEtherWithRewards * PRECISION_POINTS) - (_totalRewards * totalFee)
                                                      //
                                                      // The effect is that the given percentage of the reward goes to the fee recipient, and
                                                      // the rest of the reward is distributed between token holders proportionally to their
                                                      // token shares.
                                                      (
                                                          StakingRewardsDistribution memory rewardsDistribution,
                                                          IStakingRouter router
                                                      ) = _getStakingRewardsDistribution();
                                                      if (rewardsDistribution.totalFee > 0) {
                                                          uint256 totalPooledEtherWithRewards = _preTotalPooledEther.add(_totalRewards);
                                                          sharesMintedAsFees =
                                                              _totalRewards.mul(rewardsDistribution.totalFee).mul(_preTotalShares).div(
                                                                  totalPooledEtherWithRewards.mul(
                                                                      rewardsDistribution.precisionPoints
                                                                  ).sub(_totalRewards.mul(rewardsDistribution.totalFee))
                                                              );
                                                          _mintShares(address(this), sharesMintedAsFees);
                                                          (uint256[] memory moduleRewards, uint256 totalModuleRewards) =
                                                              _transferModuleRewards(
                                                                  rewardsDistribution.recipients,
                                                                  rewardsDistribution.modulesFees,
                                                                  rewardsDistribution.totalFee,
                                                                  sharesMintedAsFees
                                                              );
                                                          _transferTreasuryRewards(sharesMintedAsFees.sub(totalModuleRewards));
                                                          router.reportRewardsMinted(
                                                              rewardsDistribution.moduleIds,
                                                              moduleRewards
                                                          );
                                                      }
                                                  }
                                                  function _transferModuleRewards(
                                                      address[] memory recipients,
                                                      uint96[] memory modulesFees,
                                                      uint256 totalFee,
                                                      uint256 totalRewards
                                                  ) internal returns (uint256[] memory moduleRewards, uint256 totalModuleRewards) {
                                                      moduleRewards = new uint256[](recipients.length);
                                                      for (uint256 i; i < recipients.length; ++i) {
                                                          if (modulesFees[i] > 0) {
                                                              uint256 iModuleRewards = totalRewards.mul(modulesFees[i]).div(totalFee);
                                                              moduleRewards[i] = iModuleRewards;
                                                              _transferShares(address(this), recipients[i], iModuleRewards);
                                                              _emitTransferAfterMintingShares(recipients[i], iModuleRewards);
                                                              totalModuleRewards = totalModuleRewards.add(iModuleRewards);
                                                          }
                                                      }
                                                  }
                                                  function _transferTreasuryRewards(uint256 treasuryReward) internal {
                                                      address treasury = _treasury();
                                                      _transferShares(address(this), treasury, treasuryReward);
                                                      _emitTransferAfterMintingShares(treasury, treasuryReward);
                                                  }
                                                  /**
                                                   * @dev Gets the amount of Ether temporary buffered on this contract balance
                                                   */
                                                  function _getBufferedEther() internal view returns (uint256) {
                                                      return BUFFERED_ETHER_POSITION.getStorageUint256();
                                                  }
                                                  function _setBufferedEther(uint256 _newBufferedEther) internal {
                                                      BUFFERED_ETHER_POSITION.setStorageUint256(_newBufferedEther);
                                                  }
                                                  /// @dev Calculates and returns the total base balance (multiple of 32) of validators in transient state,
                                                  ///     i.e. submitted to the official Deposit contract but not yet visible in the CL state.
                                                  /// @return transient balance in wei (1e-18 Ether)
                                                  function _getTransientBalance() internal view returns (uint256) {
                                                      uint256 depositedValidators = DEPOSITED_VALIDATORS_POSITION.getStorageUint256();
                                                      uint256 clValidators = CL_VALIDATORS_POSITION.getStorageUint256();
                                                      // clValidators can never be less than deposited ones.
                                                      assert(depositedValidators >= clValidators);
                                                      return (depositedValidators - clValidators).mul(DEPOSIT_SIZE);
                                                  }
                                                  /**
                                                   * @dev Gets the total amount of Ether controlled by the system
                                                   * @return total balance in wei
                                                   */
                                                  function _getTotalPooledEther() internal view returns (uint256) {
                                                      return _getBufferedEther()
                                                          .add(CL_BALANCE_POSITION.getStorageUint256())
                                                          .add(_getTransientBalance());
                                                  }
                                                  function _pauseStaking() internal {
                                                      STAKING_STATE_POSITION.setStorageStakeLimitStruct(
                                                          STAKING_STATE_POSITION.getStorageStakeLimitStruct().setStakeLimitPauseState(true)
                                                      );
                                                      emit StakingPaused();
                                                  }
                                                  function _resumeStaking() internal {
                                                      STAKING_STATE_POSITION.setStorageStakeLimitStruct(
                                                          STAKING_STATE_POSITION.getStorageStakeLimitStruct().setStakeLimitPauseState(false)
                                                      );
                                                      emit StakingResumed();
                                                  }
                                                  function _getCurrentStakeLimit(StakeLimitState.Data memory _stakeLimitData) internal view returns (uint256) {
                                                      if (_stakeLimitData.isStakingPaused()) {
                                                          return 0;
                                                      }
                                                      if (!_stakeLimitData.isStakingLimitSet()) {
                                                          return uint256(-1);
                                                      }
                                                      return _stakeLimitData.calculateCurrentStakeLimit();
                                                  }
                                                  /**
                                                   * @dev Size-efficient analog of the `auth(_role)` modifier
                                                   * @param _role Permission name
                                                   */
                                                  function _auth(bytes32 _role) internal view {
                                                      require(canPerform(msg.sender, _role, new uint256[](0)), "APP_AUTH_FAILED");
                                                  }
                                                  /**
                                                   * @dev Intermediate data structure for `_handleOracleReport`
                                                   * Helps to overcome `stack too deep` issue.
                                                   */
                                                  struct OracleReportContext {
                                                      uint256 preCLValidators;
                                                      uint256 preCLBalance;
                                                      uint256 preTotalPooledEther;
                                                      uint256 preTotalShares;
                                                      uint256 etherToLockOnWithdrawalQueue;
                                                      uint256 sharesToBurnFromWithdrawalQueue;
                                                      uint256 simulatedSharesToBurn;
                                                      uint256 sharesToBurn;
                                                      uint256 sharesMintedAsFees;
                                                  }
                                                  /**
                                                   * @dev Handle oracle report method operating with the data-packed structs
                                                   * Using structs helps to overcome 'stack too deep' issue.
                                                   *
                                                   * The method updates the protocol's accounting state.
                                                   * Key steps:
                                                   * 1. Take a snapshot of the current (pre-) state
                                                   * 2. Pass the report data to sanity checker (reverts if malformed)
                                                   * 3. Pre-calculate the ether to lock for withdrawal queue and shares to be burnt
                                                   * 4. Pass the accounting values to sanity checker to smoothen positive token rebase
                                                   *    (i.e., postpone the extra rewards to be applied during the next rounds)
                                                   * 5. Invoke finalization of the withdrawal requests
                                                   * 6. Burn excess shares within the allowed limit (can postpone some shares to be burnt later)
                                                   * 7. Distribute protocol fee (treasury & node operators)
                                                   * 8. Complete token rebase by informing observers (emit an event and call the external receivers if any)
                                                   * 9. Sanity check for the provided simulated share rate
                                                   */
                                                  function _handleOracleReport(OracleReportedData memory _reportedData) internal returns (uint256[4]) {
                                                      OracleReportContracts memory contracts = _loadOracleReportContracts();
                                                      require(msg.sender == contracts.accountingOracle, "APP_AUTH_FAILED");
                                                      require(_reportedData.reportTimestamp <= block.timestamp, "INVALID_REPORT_TIMESTAMP");
                                                      OracleReportContext memory reportContext;
                                                      // Step 1.
                                                      // Take a snapshot of the current (pre-) state
                                                      reportContext.preTotalPooledEther = _getTotalPooledEther();
                                                      reportContext.preTotalShares = _getTotalShares();
                                                      reportContext.preCLValidators = CL_VALIDATORS_POSITION.getStorageUint256();
                                                      reportContext.preCLBalance = _processClStateUpdate(
                                                          _reportedData.reportTimestamp,
                                                          reportContext.preCLValidators,
                                                          _reportedData.clValidators,
                                                          _reportedData.postCLBalance
                                                      );
                                                      // Step 2.
                                                      // Pass the report data to sanity checker (reverts if malformed)
                                                      _checkAccountingOracleReport(contracts, _reportedData, reportContext);
                                                      // Step 3.
                                                      // Pre-calculate the ether to lock for withdrawal queue and shares to be burnt
                                                      // due to withdrawal requests to finalize
                                                      if (_reportedData.withdrawalFinalizationBatches.length != 0) {
                                                          (
                                                              reportContext.etherToLockOnWithdrawalQueue,
                                                              reportContext.sharesToBurnFromWithdrawalQueue
                                                          ) = _calculateWithdrawals(contracts, _reportedData);
                                                          if (reportContext.sharesToBurnFromWithdrawalQueue > 0) {
                                                              IBurner(contracts.burner).requestBurnShares(
                                                                  contracts.withdrawalQueue,
                                                                  reportContext.sharesToBurnFromWithdrawalQueue
                                                              );
                                                          }
                                                      }
                                                      // Step 4.
                                                      // Pass the accounting values to sanity checker to smoothen positive token rebase
                                                      uint256 withdrawals;
                                                      uint256 elRewards;
                                                      (
                                                          withdrawals, elRewards, reportContext.simulatedSharesToBurn, reportContext.sharesToBurn
                                                      ) = IOracleReportSanityChecker(contracts.oracleReportSanityChecker).smoothenTokenRebase(
                                                          reportContext.preTotalPooledEther,
                                                          reportContext.preTotalShares,
                                                          reportContext.preCLBalance,
                                                          _reportedData.postCLBalance,
                                                          _reportedData.withdrawalVaultBalance,
                                                          _reportedData.elRewardsVaultBalance,
                                                          _reportedData.sharesRequestedToBurn,
                                                          reportContext.etherToLockOnWithdrawalQueue,
                                                          reportContext.sharesToBurnFromWithdrawalQueue
                                                      );
                                                      // Step 5.
                                                      // Invoke finalization of the withdrawal requests (send ether to withdrawal queue, assign shares to be burnt)
                                                      _collectRewardsAndProcessWithdrawals(
                                                          contracts,
                                                          withdrawals,
                                                          elRewards,
                                                          _reportedData.withdrawalFinalizationBatches,
                                                          _reportedData.simulatedShareRate,
                                                          reportContext.etherToLockOnWithdrawalQueue
                                                      );
                                                      emit ETHDistributed(
                                                          _reportedData.reportTimestamp,
                                                          reportContext.preCLBalance,
                                                          _reportedData.postCLBalance,
                                                          withdrawals,
                                                          elRewards,
                                                          _getBufferedEther()
                                                      );
                                                      // Step 6.
                                                      // Burn the previously requested shares
                                                      if (reportContext.sharesToBurn > 0) {
                                                          IBurner(contracts.burner).commitSharesToBurn(reportContext.sharesToBurn);
                                                          _burnShares(contracts.burner, reportContext.sharesToBurn);
                                                      }
                                                      // Step 7.
                                                      // Distribute protocol fee (treasury & node operators)
                                                      reportContext.sharesMintedAsFees = _processRewards(
                                                          reportContext,
                                                          _reportedData.postCLBalance,
                                                          withdrawals,
                                                          elRewards
                                                      );
                                                      // Step 8.
                                                      // Complete token rebase by informing observers (emit an event and call the external receivers if any)
                                                      (
                                                          uint256 postTotalShares,
                                                          uint256 postTotalPooledEther
                                                      ) = _completeTokenRebase(
                                                          _reportedData,
                                                          reportContext,
                                                          IPostTokenRebaseReceiver(contracts.postTokenRebaseReceiver)
                                                      );
                                                      // Step 9. Sanity check for the provided simulated share rate
                                                      if (_reportedData.withdrawalFinalizationBatches.length != 0) {
                                                          IOracleReportSanityChecker(contracts.oracleReportSanityChecker).checkSimulatedShareRate(
                                                              postTotalPooledEther,
                                                              postTotalShares,
                                                              reportContext.etherToLockOnWithdrawalQueue,
                                                              reportContext.sharesToBurn.sub(reportContext.simulatedSharesToBurn),
                                                              _reportedData.simulatedShareRate
                                                          );
                                                      }
                                                      return [postTotalPooledEther, postTotalShares, withdrawals, elRewards];
                                                  }
                                                  /**
                                                   * @dev Pass the provided oracle data to the sanity checker contract
                                                   * Works with structures to overcome `stack too deep`
                                                   */
                                                  function _checkAccountingOracleReport(
                                                      OracleReportContracts memory _contracts,
                                                      OracleReportedData memory _reportedData,
                                                      OracleReportContext memory _reportContext
                                                  ) internal view {
                                                      IOracleReportSanityChecker(_contracts.oracleReportSanityChecker).checkAccountingOracleReport(
                                                          _reportedData.timeElapsed,
                                                          _reportContext.preCLBalance,
                                                          _reportedData.postCLBalance,
                                                          _reportedData.withdrawalVaultBalance,
                                                          _reportedData.elRewardsVaultBalance,
                                                          _reportedData.sharesRequestedToBurn,
                                                          _reportContext.preCLValidators,
                                                          _reportedData.clValidators
                                                      );
                                                  }
                                                  /**
                                                   * @dev Notify observers about the completed token rebase.
                                                   * Emit events and call external receivers.
                                                   */
                                                  function _completeTokenRebase(
                                                      OracleReportedData memory _reportedData,
                                                      OracleReportContext memory _reportContext,
                                                      IPostTokenRebaseReceiver _postTokenRebaseReceiver
                                                  ) internal returns (uint256 postTotalShares, uint256 postTotalPooledEther) {
                                                      postTotalShares = _getTotalShares();
                                                      postTotalPooledEther = _getTotalPooledEther();
                                                      if (_postTokenRebaseReceiver != address(0)) {
                                                          _postTokenRebaseReceiver.handlePostTokenRebase(
                                                              _reportedData.reportTimestamp,
                                                              _reportedData.timeElapsed,
                                                              _reportContext.preTotalShares,
                                                              _reportContext.preTotalPooledEther,
                                                              postTotalShares,
                                                              postTotalPooledEther,
                                                              _reportContext.sharesMintedAsFees
                                                          );
                                                      }
                                                      emit TokenRebased(
                                                          _reportedData.reportTimestamp,
                                                          _reportedData.timeElapsed,
                                                          _reportContext.preTotalShares,
                                                          _reportContext.preTotalPooledEther,
                                                          postTotalShares,
                                                          postTotalPooledEther,
                                                          _reportContext.sharesMintedAsFees
                                                      );
                                                  }
                                                  /**
                                                   * @dev Load the contracts used for `handleOracleReport` internally.
                                                   */
                                                  function _loadOracleReportContracts() internal view returns (OracleReportContracts memory ret) {
                                                      (
                                                          ret.accountingOracle,
                                                          ret.elRewardsVault,
                                                          ret.oracleReportSanityChecker,
                                                          ret.burner,
                                                          ret.withdrawalQueue,
                                                          ret.withdrawalVault,
                                                          ret.postTokenRebaseReceiver
                                                      ) = getLidoLocator().oracleReportComponentsForLido();
                                                  }
                                                  function _stakingRouter() internal view returns (IStakingRouter) {
                                                      return IStakingRouter(getLidoLocator().stakingRouter());
                                                  }
                                                  function _withdrawalQueue() internal view returns (IWithdrawalQueue) {
                                                      return IWithdrawalQueue(getLidoLocator().withdrawalQueue());
                                                  }
                                                  function _treasury() internal view returns (address) {
                                                      return getLidoLocator().treasury();
                                                  }
                                                  /**
                                                   * @notice Mints shares on behalf of 0xdead address,
                                                   * the shares amount is equal to the contract's balance.     *
                                                   *
                                                   * Allows to get rid of zero checks for `totalShares` and `totalPooledEther`
                                                   * and overcome corner cases.
                                                   *
                                                   * NB: reverts if the current contract's balance is zero.
                                                   *
                                                   * @dev must be invoked before using the token
                                                   */
                                                  function _bootstrapInitialHolder() internal {
                                                      uint256 balance = address(this).balance;
                                                      assert(balance != 0);
                                                      if (_getTotalShares() == 0) {
                                                          // if protocol is empty bootstrap it with the contract's balance
                                                          // address(0xdead) is a holder for initial shares
                                                          _setBufferedEther(balance);
                                                          // emitting `Submitted` before Transfer events to preserver events order in tx
                                                          emit Submitted(INITIAL_TOKEN_HOLDER, balance, 0);
                                                          _mintInitialShares(balance);
                                                      }
                                                  }
                                              }
                                              // SPDX-FileCopyrightText: 2023 Lido <[email protected]>
                                              // SPDX-License-Identifier: GPL-3.0
                                              /* See contracts/COMPILERS.md */
                                              pragma solidity 0.4.24;
                                              import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
                                              import "@aragon/os/contracts/common/UnstructuredStorage.sol";
                                              import "@aragon/os/contracts/lib/math/SafeMath.sol";
                                              import "./utils/Pausable.sol";
                                              /**
                                               * @title Interest-bearing ERC20-like token for Lido Liquid Stacking protocol.
                                               *
                                               * This contract is abstract. To make the contract deployable override the
                                               * `_getTotalPooledEther` function. `Lido.sol` contract inherits StETH and defines
                                               * the `_getTotalPooledEther` function.
                                               *
                                               * StETH balances are dynamic and represent the holder's share in the total amount
                                               * of Ether controlled by the protocol. Account shares aren't normalized, so the
                                               * contract also stores the sum of all shares to calculate each account's token balance
                                               * which equals to:
                                               *
                                               *   shares[account] * _getTotalPooledEther() / _getTotalShares()
                                               *
                                               * For example, assume that we have:
                                               *
                                               *   _getTotalPooledEther() -> 10 ETH
                                               *   sharesOf(user1) -> 100
                                               *   sharesOf(user2) -> 400
                                               *
                                               * Therefore:
                                               *
                                               *   balanceOf(user1) -> 2 tokens which corresponds 2 ETH
                                               *   balanceOf(user2) -> 8 tokens which corresponds 8 ETH
                                               *
                                               * Since balances of all token holders change when the amount of total pooled Ether
                                               * changes, this token cannot fully implement ERC20 standard: it only emits `Transfer`
                                               * events upon explicit transfer between holders. In contrast, when total amount of
                                               * pooled Ether increases, no `Transfer` events are generated: doing so would require
                                               * emitting an event for each token holder and thus running an unbounded loop.
                                               *
                                               * The token inherits from `Pausable` and uses `whenNotStopped` modifier for methods
                                               * which change `shares` or `allowances`. `_stop` and `_resume` functions are overridden
                                               * in `Lido.sol` and might be called by an account with the `PAUSE_ROLE` assigned by the
                                               * DAO. This is useful for emergency scenarios, e.g. a protocol bug, where one might want
                                               * to freeze all token transfers and approvals until the emergency is resolved.
                                               */
                                              contract StETH is IERC20, Pausable {
                                                  using SafeMath for uint256;
                                                  using UnstructuredStorage for bytes32;
                                                  address constant internal INITIAL_TOKEN_HOLDER = 0xdead;
                                                  uint256 constant internal INFINITE_ALLOWANCE = ~uint256(0);
                                                  /**
                                                   * @dev StETH balances are dynamic and are calculated based on the accounts' shares
                                                   * and the total amount of Ether controlled by the protocol. Account shares aren't
                                                   * normalized, so the contract also stores the sum of all shares to calculate
                                                   * each account's token balance which equals to:
                                                   *
                                                   *   shares[account] * _getTotalPooledEther() / _getTotalShares()
                                                  */
                                                  mapping (address => uint256) private shares;
                                                  /**
                                                   * @dev Allowances are nominated in tokens, not token shares.
                                                   */
                                                  mapping (address => mapping (address => uint256)) private allowances;
                                                  /**
                                                   * @dev Storage position used for holding the total amount of shares in existence.
                                                   *
                                                   * The Lido protocol is built on top of Aragon and uses the Unstructured Storage pattern
                                                   * for value types:
                                                   *
                                                   * https://blog.openzeppelin.com/upgradeability-using-unstructured-storage
                                                   * https://blog.8bitzen.com/posts/20-02-2020-understanding-how-solidity-upgradeable-unstructured-proxies-work
                                                   *
                                                   * For reference types, conventional storage variables are used since it's non-trivial
                                                   * and error-prone to implement reference-type unstructured storage using Solidity v0.4;
                                                   * see https://github.com/lidofinance/lido-dao/issues/181#issuecomment-736098834
                                                   *
                                                   * keccak256("lido.StETH.totalShares")
                                                   */
                                                  bytes32 internal constant TOTAL_SHARES_POSITION =
                                                      0xe3b4b636e601189b5f4c6742edf2538ac12bb61ed03e6da26949d69838fa447e;
                                                  /**
                                                    * @notice An executed shares transfer from `sender` to `recipient`.
                                                    *
                                                    * @dev emitted in pair with an ERC20-defined `Transfer` event.
                                                    */
                                                  event TransferShares(
                                                      address indexed from,
                                                      address indexed to,
                                                      uint256 sharesValue
                                                  );
                                                  /**
                                                   * @notice An executed `burnShares` request
                                                   *
                                                   * @dev Reports simultaneously burnt shares amount
                                                   * and corresponding stETH amount.
                                                   * The stETH amount is calculated twice: before and after the burning incurred rebase.
                                                   *
                                                   * @param account holder of the burnt shares
                                                   * @param preRebaseTokenAmount amount of stETH the burnt shares corresponded to before the burn
                                                   * @param postRebaseTokenAmount amount of stETH the burnt shares corresponded to after the burn
                                                   * @param sharesAmount amount of burnt shares
                                                   */
                                                  event SharesBurnt(
                                                      address indexed account,
                                                      uint256 preRebaseTokenAmount,
                                                      uint256 postRebaseTokenAmount,
                                                      uint256 sharesAmount
                                                  );
                                                  /**
                                                   * @return the name of the token.
                                                   */
                                                  function name() external pure returns (string) {
                                                      return "Liquid staked Ether 2.0";
                                                  }
                                                  /**
                                                   * @return the symbol of the token, usually a shorter version of the
                                                   * name.
                                                   */
                                                  function symbol() external pure returns (string) {
                                                      return "stETH";
                                                  }
                                                  /**
                                                   * @return the number of decimals for getting user representation of a token amount.
                                                   */
                                                  function decimals() external pure returns (uint8) {
                                                      return 18;
                                                  }
                                                  /**
                                                   * @return the amount of tokens in existence.
                                                   *
                                                   * @dev Always equals to `_getTotalPooledEther()` since token amount
                                                   * is pegged to the total amount of Ether controlled by the protocol.
                                                   */
                                                  function totalSupply() external view returns (uint256) {
                                                      return _getTotalPooledEther();
                                                  }
                                                  /**
                                                   * @return the entire amount of Ether controlled by the protocol.
                                                   *
                                                   * @dev The sum of all ETH balances in the protocol, equals to the total supply of stETH.
                                                   */
                                                  function getTotalPooledEther() external view returns (uint256) {
                                                      return _getTotalPooledEther();
                                                  }
                                                  /**
                                                   * @return the amount of tokens owned by the `_account`.
                                                   *
                                                   * @dev Balances are dynamic and equal the `_account`'s share in the amount of the
                                                   * total Ether controlled by the protocol. See `sharesOf`.
                                                   */
                                                  function balanceOf(address _account) external view returns (uint256) {
                                                      return getPooledEthByShares(_sharesOf(_account));
                                                  }
                                                  /**
                                                   * @notice Moves `_amount` tokens from the caller's account to the `_recipient` account.
                                                   *
                                                   * @return a boolean value indicating whether the operation succeeded.
                                                   * Emits a `Transfer` event.
                                                   * Emits a `TransferShares` event.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `_recipient` cannot be the zero address.
                                                   * - the caller must have a balance of at least `_amount`.
                                                   * - the contract must not be paused.
                                                   *
                                                   * @dev The `_amount` argument is the amount of tokens, not shares.
                                                   */
                                                  function transfer(address _recipient, uint256 _amount) external returns (bool) {
                                                      _transfer(msg.sender, _recipient, _amount);
                                                      return true;
                                                  }
                                                  /**
                                                   * @return the remaining number of tokens that `_spender` is allowed to spend
                                                   * on behalf of `_owner` through `transferFrom`. This is zero by default.
                                                   *
                                                   * @dev This value changes when `approve` or `transferFrom` is called.
                                                   */
                                                  function allowance(address _owner, address _spender) external view returns (uint256) {
                                                      return allowances[_owner][_spender];
                                                  }
                                                  /**
                                                   * @notice Sets `_amount` as the allowance of `_spender` over the caller's tokens.
                                                   *
                                                   * @return a boolean value indicating whether the operation succeeded.
                                                   * Emits an `Approval` event.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `_spender` cannot be the zero address.
                                                   *
                                                   * @dev The `_amount` argument is the amount of tokens, not shares.
                                                   */
                                                  function approve(address _spender, uint256 _amount) external returns (bool) {
                                                      _approve(msg.sender, _spender, _amount);
                                                      return true;
                                                  }
                                                  /**
                                                   * @notice Moves `_amount` tokens from `_sender` to `_recipient` using the
                                                   * allowance mechanism. `_amount` is then deducted from the caller's
                                                   * allowance.
                                                   *
                                                   * @return a boolean value indicating whether the operation succeeded.
                                                   *
                                                   * Emits a `Transfer` event.
                                                   * Emits a `TransferShares` event.
                                                   * Emits an `Approval` event indicating the updated allowance.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `_sender` and `_recipient` cannot be the zero addresses.
                                                   * - `_sender` must have a balance of at least `_amount`.
                                                   * - the caller must have allowance for `_sender`'s tokens of at least `_amount`.
                                                   * - the contract must not be paused.
                                                   *
                                                   * @dev The `_amount` argument is the amount of tokens, not shares.
                                                   */
                                                  function transferFrom(address _sender, address _recipient, uint256 _amount) external returns (bool) {
                                                      _spendAllowance(_sender, msg.sender, _amount);
                                                      _transfer(_sender, _recipient, _amount);
                                                      return true;
                                                  }
                                                  /**
                                                   * @notice Atomically increases the allowance granted to `_spender` by the caller by `_addedValue`.
                                                   *
                                                   * This is an alternative to `approve` that can be used as a mitigation for
                                                   * problems described in:
                                                   * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/b709eae01d1da91902d06ace340df6b324e6f049/contracts/token/ERC20/IERC20.sol#L57
                                                   * Emits an `Approval` event indicating the updated allowance.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `_spender` cannot be the the zero address.
                                                   */
                                                  function increaseAllowance(address _spender, uint256 _addedValue) external returns (bool) {
                                                      _approve(msg.sender, _spender, allowances[msg.sender][_spender].add(_addedValue));
                                                      return true;
                                                  }
                                                  /**
                                                   * @notice Atomically decreases the allowance granted to `_spender` by the caller by `_subtractedValue`.
                                                   *
                                                   * This is an alternative to `approve` that can be used as a mitigation for
                                                   * problems described in:
                                                   * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/b709eae01d1da91902d06ace340df6b324e6f049/contracts/token/ERC20/IERC20.sol#L57
                                                   * Emits an `Approval` event indicating the updated allowance.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `_spender` cannot be the zero address.
                                                   * - `_spender` must have allowance for the caller of at least `_subtractedValue`.
                                                   */
                                                  function decreaseAllowance(address _spender, uint256 _subtractedValue) external returns (bool) {
                                                      uint256 currentAllowance = allowances[msg.sender][_spender];
                                                      require(currentAllowance >= _subtractedValue, "ALLOWANCE_BELOW_ZERO");
                                                      _approve(msg.sender, _spender, currentAllowance.sub(_subtractedValue));
                                                      return true;
                                                  }
                                                  /**
                                                   * @return the total amount of shares in existence.
                                                   *
                                                   * @dev The sum of all accounts' shares can be an arbitrary number, therefore
                                                   * it is necessary to store it in order to calculate each account's relative share.
                                                   */
                                                  function getTotalShares() external view returns (uint256) {
                                                      return _getTotalShares();
                                                  }
                                                  /**
                                                   * @return the amount of shares owned by `_account`.
                                                   */
                                                  function sharesOf(address _account) external view returns (uint256) {
                                                      return _sharesOf(_account);
                                                  }
                                                  /**
                                                   * @return the amount of shares that corresponds to `_ethAmount` protocol-controlled Ether.
                                                   */
                                                  function getSharesByPooledEth(uint256 _ethAmount) public view returns (uint256) {
                                                      return _ethAmount
                                                          .mul(_getTotalShares())
                                                          .div(_getTotalPooledEther());
                                                  }
                                                  /**
                                                   * @return the amount of Ether that corresponds to `_sharesAmount` token shares.
                                                   */
                                                  function getPooledEthByShares(uint256 _sharesAmount) public view returns (uint256) {
                                                      return _sharesAmount
                                                          .mul(_getTotalPooledEther())
                                                          .div(_getTotalShares());
                                                  }
                                                  /**
                                                   * @notice Moves `_sharesAmount` token shares from the caller's account to the `_recipient` account.
                                                   *
                                                   * @return amount of transferred tokens.
                                                   * Emits a `TransferShares` event.
                                                   * Emits a `Transfer` event.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `_recipient` cannot be the zero address.
                                                   * - the caller must have at least `_sharesAmount` shares.
                                                   * - the contract must not be paused.
                                                   *
                                                   * @dev The `_sharesAmount` argument is the amount of shares, not tokens.
                                                   */
                                                  function transferShares(address _recipient, uint256 _sharesAmount) external returns (uint256) {
                                                      _transferShares(msg.sender, _recipient, _sharesAmount);
                                                      uint256 tokensAmount = getPooledEthByShares(_sharesAmount);
                                                      _emitTransferEvents(msg.sender, _recipient, tokensAmount, _sharesAmount);
                                                      return tokensAmount;
                                                  }
                                                  /**
                                                   * @notice Moves `_sharesAmount` token shares from the `_sender` account to the `_recipient` account.
                                                   *
                                                   * @return amount of transferred tokens.
                                                   * Emits a `TransferShares` event.
                                                   * Emits a `Transfer` event.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `_sender` and `_recipient` cannot be the zero addresses.
                                                   * - `_sender` must have at least `_sharesAmount` shares.
                                                   * - the caller must have allowance for `_sender`'s tokens of at least `getPooledEthByShares(_sharesAmount)`.
                                                   * - the contract must not be paused.
                                                   *
                                                   * @dev The `_sharesAmount` argument is the amount of shares, not tokens.
                                                   */
                                                  function transferSharesFrom(
                                                      address _sender, address _recipient, uint256 _sharesAmount
                                                  ) external returns (uint256) {
                                                      uint256 tokensAmount = getPooledEthByShares(_sharesAmount);
                                                      _spendAllowance(_sender, msg.sender, tokensAmount);
                                                      _transferShares(_sender, _recipient, _sharesAmount);
                                                      _emitTransferEvents(_sender, _recipient, tokensAmount, _sharesAmount);
                                                      return tokensAmount;
                                                  }
                                                  /**
                                                   * @return the total amount (in wei) of Ether controlled by the protocol.
                                                   * @dev This is used for calculating tokens from shares and vice versa.
                                                   * @dev This function is required to be implemented in a derived contract.
                                                   */
                                                  function _getTotalPooledEther() internal view returns (uint256);
                                                  /**
                                                   * @notice Moves `_amount` tokens from `_sender` to `_recipient`.
                                                   * Emits a `Transfer` event.
                                                   * Emits a `TransferShares` event.
                                                   */
                                                  function _transfer(address _sender, address _recipient, uint256 _amount) internal {
                                                      uint256 _sharesToTransfer = getSharesByPooledEth(_amount);
                                                      _transferShares(_sender, _recipient, _sharesToTransfer);
                                                      _emitTransferEvents(_sender, _recipient, _amount, _sharesToTransfer);
                                                  }
                                                  /**
                                                   * @notice Sets `_amount` as the allowance of `_spender` over the `_owner` s tokens.
                                                   *
                                                   * Emits an `Approval` event.
                                                   *
                                                   * NB: the method can be invoked even if the protocol paused.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `_owner` cannot be the zero address.
                                                   * - `_spender` cannot be the zero address.
                                                   */
                                                  function _approve(address _owner, address _spender, uint256 _amount) internal {
                                                      require(_owner != address(0), "APPROVE_FROM_ZERO_ADDR");
                                                      require(_spender != address(0), "APPROVE_TO_ZERO_ADDR");
                                                      allowances[_owner][_spender] = _amount;
                                                      emit Approval(_owner, _spender, _amount);
                                                  }
                                                  /**
                                                   * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
                                                   *
                                                   * Does not update the allowance amount in case of infinite allowance.
                                                   * Revert if not enough allowance is available.
                                                   *
                                                   * Might emit an {Approval} event.
                                                   */
                                                  function _spendAllowance(address _owner, address _spender, uint256 _amount) internal {
                                                      uint256 currentAllowance = allowances[_owner][_spender];
                                                      if (currentAllowance != INFINITE_ALLOWANCE) {
                                                          require(currentAllowance >= _amount, "ALLOWANCE_EXCEEDED");
                                                          _approve(_owner, _spender, currentAllowance - _amount);
                                                      }
                                                  }
                                                  /**
                                                   * @return the total amount of shares in existence.
                                                   */
                                                  function _getTotalShares() internal view returns (uint256) {
                                                      return TOTAL_SHARES_POSITION.getStorageUint256();
                                                  }
                                                  /**
                                                   * @return the amount of shares owned by `_account`.
                                                   */
                                                  function _sharesOf(address _account) internal view returns (uint256) {
                                                      return shares[_account];
                                                  }
                                                  /**
                                                   * @notice Moves `_sharesAmount` shares from `_sender` to `_recipient`.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `_sender` cannot be the zero address.
                                                   * - `_recipient` cannot be the zero address or the `stETH` token contract itself
                                                   * - `_sender` must hold at least `_sharesAmount` shares.
                                                   * - the contract must not be paused.
                                                   */
                                                  function _transferShares(address _sender, address _recipient, uint256 _sharesAmount) internal {
                                                      require(_sender != address(0), "TRANSFER_FROM_ZERO_ADDR");
                                                      require(_recipient != address(0), "TRANSFER_TO_ZERO_ADDR");
                                                      require(_recipient != address(this), "TRANSFER_TO_STETH_CONTRACT");
                                                      _whenNotStopped();
                                                      uint256 currentSenderShares = shares[_sender];
                                                      require(_sharesAmount <= currentSenderShares, "BALANCE_EXCEEDED");
                                                      shares[_sender] = currentSenderShares.sub(_sharesAmount);
                                                      shares[_recipient] = shares[_recipient].add(_sharesAmount);
                                                  }
                                                  /**
                                                   * @notice Creates `_sharesAmount` shares and assigns them to `_recipient`, increasing the total amount of shares.
                                                   * @dev This doesn't increase the token total supply.
                                                   *
                                                   * NB: The method doesn't check protocol pause relying on the external enforcement.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `_recipient` cannot be the zero address.
                                                   * - the contract must not be paused.
                                                   */
                                                  function _mintShares(address _recipient, uint256 _sharesAmount) internal returns (uint256 newTotalShares) {
                                                      require(_recipient != address(0), "MINT_TO_ZERO_ADDR");
                                                      newTotalShares = _getTotalShares().add(_sharesAmount);
                                                      TOTAL_SHARES_POSITION.setStorageUint256(newTotalShares);
                                                      shares[_recipient] = shares[_recipient].add(_sharesAmount);
                                                      // Notice: we're not emitting a Transfer event from the zero address here since shares mint
                                                      // works by taking the amount of tokens corresponding to the minted shares from all other
                                                      // token holders, proportionally to their share. The total supply of the token doesn't change
                                                      // as the result. This is equivalent to performing a send from each other token holder's
                                                      // address to `address`, but we cannot reflect this as it would require sending an unbounded
                                                      // number of events.
                                                  }
                                                  /**
                                                   * @notice Destroys `_sharesAmount` shares from `_account`'s holdings, decreasing the total amount of shares.
                                                   * @dev This doesn't decrease the token total supply.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `_account` cannot be the zero address.
                                                   * - `_account` must hold at least `_sharesAmount` shares.
                                                   * - the contract must not be paused.
                                                   */
                                                  function _burnShares(address _account, uint256 _sharesAmount) internal returns (uint256 newTotalShares) {
                                                      require(_account != address(0), "BURN_FROM_ZERO_ADDR");
                                                      uint256 accountShares = shares[_account];
                                                      require(_sharesAmount <= accountShares, "BALANCE_EXCEEDED");
                                                      uint256 preRebaseTokenAmount = getPooledEthByShares(_sharesAmount);
                                                      newTotalShares = _getTotalShares().sub(_sharesAmount);
                                                      TOTAL_SHARES_POSITION.setStorageUint256(newTotalShares);
                                                      shares[_account] = accountShares.sub(_sharesAmount);
                                                      uint256 postRebaseTokenAmount = getPooledEthByShares(_sharesAmount);
                                                      emit SharesBurnt(_account, preRebaseTokenAmount, postRebaseTokenAmount, _sharesAmount);
                                                      // Notice: we're not emitting a Transfer event to the zero address here since shares burn
                                                      // works by redistributing the amount of tokens corresponding to the burned shares between
                                                      // all other token holders. The total supply of the token doesn't change as the result.
                                                      // This is equivalent to performing a send from `address` to each other token holder address,
                                                      // but we cannot reflect this as it would require sending an unbounded number of events.
                                                      // We're emitting `SharesBurnt` event to provide an explicit rebase log record nonetheless.
                                                  }
                                                  /**
                                                   * @dev Emits {Transfer} and {TransferShares} events
                                                   */
                                                  function _emitTransferEvents(address _from, address _to, uint _tokenAmount, uint256 _sharesAmount) internal {
                                                      emit Transfer(_from, _to, _tokenAmount);
                                                      emit TransferShares(_from, _to, _sharesAmount);
                                                  }
                                                  /**
                                                   * @dev Emits {Transfer} and {TransferShares} events where `from` is 0 address. Indicates mint events.
                                                   */
                                                  function _emitTransferAfterMintingShares(address _to, uint256 _sharesAmount) internal {
                                                      _emitTransferEvents(address(0), _to, getPooledEthByShares(_sharesAmount), _sharesAmount);
                                                  }
                                                  /**
                                                   * @dev Mints shares to INITIAL_TOKEN_HOLDER
                                                   */
                                                  function _mintInitialShares(uint256 _sharesAmount) internal {
                                                      _mintShares(INITIAL_TOKEN_HOLDER, _sharesAmount);
                                                      _emitTransferAfterMintingShares(INITIAL_TOKEN_HOLDER, _sharesAmount);
                                                  }
                                              }
                                              // SPDX-FileCopyrightText: 2023 OpenZeppelin, Lido <[email protected]>
                                              // SPDX-License-Identifier: GPL-3.0
                                              /* See contracts/COMPILERS.md */
                                              pragma solidity 0.4.24;
                                              import {UnstructuredStorage} from "@aragon/os/contracts/common/UnstructuredStorage.sol";
                                              import {SignatureUtils} from "../common/lib/SignatureUtils.sol";
                                              import {IEIP712StETH} from "../common/interfaces/IEIP712StETH.sol";
                                              import {StETH} from "./StETH.sol";
                                              /**
                                               * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                                               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                                               *
                                               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                                               * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                                               * need to send a transaction, and thus is not required to hold Ether at all.
                                               */
                                              interface IERC2612 {
                                                  /**
                                                   * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                                                   * given ``owner``'s signed approval.
                                                   * Emits an {Approval} event.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `spender` cannot be the zero address.
                                                   * - `deadline` must be a timestamp in the future.
                                                   * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                                                   * over the EIP712-formatted function arguments.
                                                   * - the signature must use ``owner``'s current nonce (see {nonces}).
                                                   */
                                                  function permit(
                                                      address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s
                                                  ) external;
                                                  /**
                                                   * @dev Returns the current nonce for `owner`. This value must be
                                                   * included whenever a signature is generated for {permit}.
                                                   *
                                                   * Every successful call to {permit} increases ``owner``'s nonce by one. This
                                                   * prevents a signature from being used multiple times.
                                                   */
                                                  function nonces(address owner) external view returns (uint256);
                                                  /**
                                                   * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                                                   */
                                                  // solhint-disable-next-line func-name-mixedcase
                                                  function DOMAIN_SEPARATOR() external view returns (bytes32);
                                              }
                                              contract StETHPermit is IERC2612, StETH {
                                                  using UnstructuredStorage for bytes32;
                                                  /**
                                                   * @dev Service event for initialization
                                                   */
                                                  event EIP712StETHInitialized(address eip712StETH);
                                                  /**
                                                   * @dev Nonces for ERC-2612 (Permit)
                                                   */
                                                  mapping(address => uint256) internal noncesByAddress;
                                                  /**
                                                   * @dev Storage position used for the EIP712 message utils contract
                                                   *
                                                   * keccak256("lido.StETHPermit.eip712StETH")
                                                   */
                                                  bytes32 internal constant EIP712_STETH_POSITION =
                                                      0x42b2d95e1ce15ce63bf9a8d9f6312cf44b23415c977ffa3b884333422af8941c;
                                                  /**
                                                   * @dev Typehash constant for ERC-2612 (Permit)
                                                   *
                                                   * keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
                                                   */
                                                  bytes32 internal constant PERMIT_TYPEHASH =
                                                      0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
                                                  /**
                                                   * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                                                   * given ``owner``'s signed approval.
                                                   * Emits an {Approval} event.
                                                   *
                                                   * Requirements:
                                                   *
                                                   * - `spender` cannot be the zero address.
                                                   * - `deadline` must be a timestamp in the future.
                                                   * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                                                   * over the EIP712-formatted function arguments.
                                                   * - the signature must use ``owner``'s current nonce (see {nonces}).
                                                   */
                                                  function permit(
                                                      address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s
                                                  ) external {
                                                      require(block.timestamp <= _deadline, "DEADLINE_EXPIRED");
                                                      bytes32 structHash = keccak256(
                                                          abi.encode(PERMIT_TYPEHASH, _owner, _spender, _value, _useNonce(_owner), _deadline)
                                                      );
                                                      bytes32 hash = IEIP712StETH(getEIP712StETH()).hashTypedDataV4(address(this), structHash);
                                                      require(SignatureUtils.isValidSignature(_owner, hash, _v, _r, _s), "INVALID_SIGNATURE");
                                                      _approve(_owner, _spender, _value);
                                                  }
                                                  /**
                                                   * @dev Returns the current nonce for `owner`. This value must be
                                                   * included whenever a signature is generated for {permit}.
                                                   *
                                                   * Every successful call to {permit} increases ``owner``'s nonce by one. This
                                                   * prevents a signature from being used multiple times.
                                                   */
                                                  function nonces(address owner) external view returns (uint256) {
                                                      return noncesByAddress[owner];
                                                  }
                                                  /**
                                                   * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                                                   */
                                                  // solhint-disable-next-line func-name-mixedcase
                                                  function DOMAIN_SEPARATOR() external view returns (bytes32) {
                                                      return IEIP712StETH(getEIP712StETH()).domainSeparatorV4(address(this));
                                                  }
                                                  /**
                                                   * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
                                                   * signature.
                                                   *
                                                   * NB: compairing to the full-fledged ERC-5267 version:
                                                   * - `salt` and `extensions` are unused
                                                   * - `flags` is hex"0f" or 01111b
                                                   *
                                                   * @dev using shortened returns to reduce a bytecode size
                                                   */
                                                  function eip712Domain() external view returns (
                                                      string memory name,
                                                      string memory version,
                                                      uint256 chainId,
                                                      address verifyingContract
                                                  ) {
                                                      return IEIP712StETH(getEIP712StETH()).eip712Domain(address(this));
                                                  }
                                                  /**
                                                   * @dev "Consume a nonce": return the current value and increment.
                                                   */
                                                  function _useNonce(address _owner) internal returns (uint256 current) {
                                                      current = noncesByAddress[_owner];
                                                      noncesByAddress[_owner] = current.add(1);
                                                  }
                                                  /**
                                                   * @dev Initialize EIP712 message utils contract for stETH
                                                   */
                                                  function _initializeEIP712StETH(address _eip712StETH) internal {
                                                      require(_eip712StETH != address(0), "ZERO_EIP712STETH");
                                                      require(getEIP712StETH() == address(0), "EIP712STETH_ALREADY_SET");
                                                      EIP712_STETH_POSITION.setStorageAddress(_eip712StETH);
                                                      emit EIP712StETHInitialized(_eip712StETH);
                                                  }
                                                  /**
                                                   * @dev Get EIP712 message utils contract
                                                   */
                                                  function getEIP712StETH() public view returns (address) {
                                                      return EIP712_STETH_POSITION.getStorageAddress();
                                                  }
                                              }
                                              // SPDX-FileCopyrightText: 2023 Lido <[email protected]>
                                              // SPDX-License-Identifier: GPL-3.0
                                              pragma solidity 0.4.24;
                                              import "@aragon/os/contracts/common/UnstructuredStorage.sol";
                                              contract Pausable {
                                                  using UnstructuredStorage for bytes32;
                                                  event Stopped();
                                                  event Resumed();
                                                  // keccak256("lido.Pausable.activeFlag")
                                                  bytes32 internal constant ACTIVE_FLAG_POSITION =
                                                      0x644132c4ddd5bb6f0655d5fe2870dcec7870e6be4758890f366b83441f9fdece;
                                                  function _whenNotStopped() internal view {
                                                      require(ACTIVE_FLAG_POSITION.getStorageBool(), "CONTRACT_IS_STOPPED");
                                                  }
                                                  function _whenStopped() internal view {
                                                      require(!ACTIVE_FLAG_POSITION.getStorageBool(), "CONTRACT_IS_ACTIVE");
                                                  }
                                                  function isStopped() public view returns (bool) {
                                                      return !ACTIVE_FLAG_POSITION.getStorageBool();
                                                  }
                                                  function _stop() internal {
                                                      _whenNotStopped();
                                                      ACTIVE_FLAG_POSITION.setStorageBool(false);
                                                      emit Stopped();
                                                  }
                                                  function _resume() internal {
                                                      _whenStopped();
                                                      ACTIVE_FLAG_POSITION.setStorageBool(true);
                                                      emit Resumed();
                                                  }
                                              }
                                              // SPDX-FileCopyrightText: 2023 Lido <[email protected]>
                                              // SPDX-License-Identifier: GPL-3.0
                                              pragma solidity 0.4.24;
                                              import "@aragon/os/contracts/common/UnstructuredStorage.sol";
                                              /**
                                               * @title Adapted code of /contracts/0.8.9/utils/Versioned.sol
                                               *
                                               * This contract contains only core part of original Versioned.sol
                                               * to reduce contract size
                                               */
                                              contract Versioned {
                                                  using UnstructuredStorage for bytes32;
                                                  event ContractVersionSet(uint256 version);
                                                  /// @dev Storage slot: uint256 version
                                                  /// Version of the initialized contract storage.
                                                  /// The version stored in CONTRACT_VERSION_POSITION equals to:
                                                  /// - 0 right after the deployment, before an initializer is invoked (and only at that moment);
                                                  /// - N after calling initialize(), where N is the initially deployed contract version;
                                                  /// - N after upgrading contract by calling finalizeUpgrade_vN().
                                                  bytes32 internal constant CONTRACT_VERSION_POSITION =
                                                      0x4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a6; // keccak256("lido.Versioned.contractVersion");
                                                  uint256 internal constant PETRIFIED_VERSION_MARK = uint256(-1);
                                                  constructor() public {
                                                      // lock version in the implementation's storage to prevent initialization
                                                      CONTRACT_VERSION_POSITION.setStorageUint256(PETRIFIED_VERSION_MARK);
                                                  }
                                                  /// @notice Returns the current contract version.
                                                  function getContractVersion() public view returns (uint256) {
                                                      return CONTRACT_VERSION_POSITION.getStorageUint256();
                                                  }
                                                  function _checkContractVersion(uint256 version) internal view {
                                                      require(version == getContractVersion(), "UNEXPECTED_CONTRACT_VERSION");
                                                  }
                                                  function _setContractVersion(uint256 version) internal {
                                                      CONTRACT_VERSION_POSITION.setStorageUint256(version);
                                                      emit ContractVersionSet(version);
                                                  }
                                              }
                                              // SPDX-FileCopyrightText: 2023 Lido <[email protected]>
                                              // SPDX-License-Identifier: GPL-3.0
                                              // See contracts/COMPILERS.md
                                              // solhint-disable-next-line
                                              pragma solidity >=0.4.24 <0.9.0;
                                              interface IBurner {
                                                  /**
                                                   * Commit cover/non-cover burning requests and logs cover/non-cover shares amount just burnt.
                                                   *
                                                   * NB: The real burn enactment to be invoked after the call (via internal Lido._burnShares())
                                                   */
                                                  function commitSharesToBurn(uint256 _stETHSharesToBurn) external;
                                                  /**
                                                   * Request burn shares
                                                   */
                                                  function requestBurnShares(address _from, uint256 _sharesAmount) external;
                                                  /**
                                                    * Returns the current amount of shares locked on the contract to be burnt.
                                                    */
                                                  function getSharesRequestedToBurn() external view returns (uint256 coverShares, uint256 nonCoverShares);
                                                  /**
                                                    * Returns the total cover shares ever burnt.
                                                    */
                                                  function getCoverSharesBurnt() external view returns (uint256);
                                                  /**
                                                    * Returns the total non-cover shares ever burnt.
                                                    */
                                                  function getNonCoverSharesBurnt() external view returns (uint256);
                                              }
                                              // SPDX-FileCopyrightText: 2023 OpenZeppelin, Lido <[email protected]>
                                              // SPDX-License-Identifier: GPL-3.0
                                              // See contracts/COMPILERS.md
                                              // solhint-disable-next-line
                                              pragma solidity >=0.4.24 <0.9.0;
                                              /**
                                               * @dev Helper interface of EIP712 StETH-dedicated helper.
                                               *
                                               * Has an access to the CHAIN_ID opcode and relies on immutables internally
                                               * Both are unavailable for Solidity 0.4.24.
                                               */
                                              interface IEIP712StETH {
                                                  /**
                                                   * @dev Returns the domain separator for the current chain.
                                                   */
                                                  function domainSeparatorV4(address _stETH) external view returns (bytes32);
                                                  /**
                                                   * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                                                   * function returns the hash of the fully encoded EIP712 message for this domain.
                                                   *
                                                   * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                                                   *
                                                   * ```solidity
                                                   * bytes32 digest = hashTypedDataV4(keccak256(abi.encode(
                                                   *     keccak256("Mail(address to,string contents)"),
                                                   *     mailTo,
                                                   *     keccak256(bytes(mailContents))
                                                   * )));
                                                   * address signer = ECDSA.recover(digest, signature);
                                                   * ```
                                                   */
                                                  function hashTypedDataV4(address _stETH, bytes32 _structHash) external view returns (bytes32);
                                                  /**
                                                   * @dev returns the fields and values that describe the domain separator
                                                   * used by stETH for EIP-712 signature.
                                                   */
                                                  function eip712Domain(address _stETH) external view returns (
                                                      string memory name,
                                                      string memory version,
                                                      uint256 chainId,
                                                      address verifyingContract
                                                  );
                                              }
                                              // SPDX-FileCopyrightText: 2023 Lido <[email protected]>
                                              // SPDX-License-Identifier: GPL-3.0
                                              // See contracts/COMPILERS.md
                                              // solhint-disable-next-line
                                              pragma solidity >=0.4.24 <0.9.0;
                                              interface ILidoLocator {
                                                  function accountingOracle() external view returns(address);
                                                  function depositSecurityModule() external view returns(address);
                                                  function elRewardsVault() external view returns(address);
                                                  function legacyOracle() external view returns(address);
                                                  function lido() external view returns(address);
                                                  function oracleReportSanityChecker() external view returns(address);
                                                  function burner() external view returns(address);
                                                  function stakingRouter() external view returns(address);
                                                  function treasury() external view returns(address);
                                                  function validatorsExitBusOracle() external view returns(address);
                                                  function withdrawalQueue() external view returns(address);
                                                  function withdrawalVault() external view returns(address);
                                                  function postTokenRebaseReceiver() external view returns(address);
                                                  function oracleDaemonConfig() external view returns(address);
                                                  function coreComponents() external view returns(
                                                      address elRewardsVault,
                                                      address oracleReportSanityChecker,
                                                      address stakingRouter,
                                                      address treasury,
                                                      address withdrawalQueue,
                                                      address withdrawalVault
                                                  );
                                                  function oracleReportComponentsForLido() external view returns(
                                                      address accountingOracle,
                                                      address elRewardsVault,
                                                      address oracleReportSanityChecker,
                                                      address burner,
                                                      address withdrawalQueue,
                                                      address withdrawalVault,
                                                      address postTokenRebaseReceiver
                                                  );
                                              }
                                              // SPDX-License-Identifier: MIT
                                              // Extracted from:
                                              // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/cryptography/ECDSA.sol#L53
                                              // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/541e821/contracts/utils/cryptography/ECDSA.sol#L112
                                              /* See contracts/COMPILERS.md */
                                              // solhint-disable-next-line
                                              pragma solidity >=0.4.24 <0.9.0;
                                              library ECDSA {
                                                  /**
                                                   * @dev Returns the address that signed a hashed message (`hash`).
                                                   * This address can then be used for verification purposes.
                                                   * Receives the `v`, `r` and `s` signature fields separately.
                                                   *
                                                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                                                   * this function rejects them by requiring the `s` value to be in the lower
                                                   * half order, and the `v` value to be either 27 or 28.
                                                   *
                                                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                                                   * verification to be secure: it is possible to craft signatures that
                                                   * recover to arbitrary addresses for non-hashed data.
                                                   */
                                                  function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address)
                                                  {
                                                      // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                                                      // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                                                      // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
                                                      // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                                                      //
                                                      // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                                                      // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                                                      // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                                                      // these malleable signatures as well.
                                                      require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
                                                      // If the signature is valid (and not malleable), return the signer address
                                                      address signer = ecrecover(hash, v, r, s);
                                                      require(signer != address(0), "ECDSA: invalid signature");
                                                      return signer;
                                                  }
                                                  /**
                                                   * @dev Overload of `recover` that receives the `r` and `vs` short-signature fields separately.
                                                   * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
                                                   */
                                                  function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
                                                      bytes32 s;
                                                      uint8 v;
                                                      assembly {
                                                          s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
                                                          v := add(shr(255, vs), 27)
                                                      }
                                                      return recover(hash, v, r, s);
                                                  }
                                              }
                                              // SPDX-FileCopyrightText: 2023 Lido <[email protected]>
                                              // SPDX-License-Identifier: MIT
                                              // Copied from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0457042d93d9dfd760dbaa06a4d2f1216fdbe297/contracts/utils/math/Math.sol
                                              // See contracts/COMPILERS.md
                                              // solhint-disable-next-line
                                              pragma solidity >=0.4.24 <0.9.0;
                                              library Math256 {
                                                  /// @dev Returns the largest of two numbers.
                                                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                                                      return a > b ? a : b;
                                                  }
                                                  /// @dev Returns the smallest of two numbers.
                                                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                                                      return a < b ? a : b;
                                                  }
                                                  /// @dev Returns the largest of two numbers.
                                                  function max(int256 a, int256 b) internal pure returns (int256) {
                                                      return a > b ? a : b;
                                                  }
                                                  /// @dev Returns the smallest of two numbers.
                                                  function min(int256 a, int256 b) internal pure returns (int256) {
                                                      return a < b ? a : b;
                                                  }
                                                  /// @dev Returns the ceiling of the division of two numbers.
                                                  ///
                                                  /// This differs from standard division with `/` in that it rounds up instead
                                                  /// of rounding down.
                                                  function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                                                      // (a + b - 1) / b can overflow on addition, so we distribute.
                                                      return a == 0 ? 0 : (a - 1) / b + 1;
                                                  }
                                                  /// @dev Returns absolute difference of two numbers.
                                                  function absDiff(uint256 a, uint256 b) internal pure returns (uint256) {
                                                      return a > b ? a - b : b - a;
                                                  }
                                              }
                                              // SPDX-FileCopyrightText: 2023 Lido <[email protected]>
                                              // SPDX-License-Identifier: MIT
                                              /* See contracts/COMPILERS.md */
                                              // solhint-disable-next-line lido/fixed-compiler-version
                                              pragma solidity >=0.4.24 <0.9.0;
                                              import {ECDSA} from "./ECDSA.sol";
                                              library SignatureUtils {
                                                  /**
                                                   * @dev The selector of the ERC1271's `isValidSignature(bytes32 hash, bytes signature)` function,
                                                   * serving at the same time as the magic value that the function should return upon success.
                                                   *
                                                   * See https://eips.ethereum.org/EIPS/eip-1271.
                                                   *
                                                   * bytes4(keccak256("isValidSignature(bytes32,bytes)")
                                                   */
                                                  bytes4 internal constant ERC1271_IS_VALID_SIGNATURE_SELECTOR = 0x1626ba7e;
                                                  /**
                                                   * @dev Checks signature validity.
                                                   *
                                                   * If the signer address doesn't contain any code, assumes that the address is externally owned
                                                   * and the signature is a ECDSA signature generated using its private key. Otherwise, issues a
                                                   * static call to the signer address to check the signature validity using the ERC-1271 standard.
                                                   */
                                                  function isValidSignature(
                                                      address signer,
                                                      bytes32 msgHash,
                                                      uint8 v,
                                                      bytes32 r,
                                                      bytes32 s
                                                  ) internal view returns (bool) {
                                                      if (_hasCode(signer)) {
                                                          bytes memory sig = abi.encodePacked(r, s, v);
                                                          // Solidity <0.5 generates a regular CALL instruction even if the function being called
                                                          // is marked as `view`, and the only way to perform a STATICCALL is to use assembly
                                                          bytes memory data = abi.encodeWithSelector(ERC1271_IS_VALID_SIGNATURE_SELECTOR, msgHash, sig);
                                                          bytes32 retval;
                                                          /// @solidity memory-safe-assembly
                                                          assembly {
                                                              // allocate memory for storing the return value
                                                              let outDataOffset := mload(0x40)
                                                              mstore(0x40, add(outDataOffset, 32))
                                                              // issue a static call and load the result if the call succeeded
                                                              let success := staticcall(gas(), signer, add(data, 32), mload(data), outDataOffset, 32)
                                                              if and(eq(success, 1), eq(returndatasize(), 32)) {
                                                                  retval := mload(outDataOffset)
                                                              }
                                                          }
                                                          return retval == bytes32(ERC1271_IS_VALID_SIGNATURE_SELECTOR);
                                                      } else {
                                                          return ECDSA.recover(msgHash, v, r, s) == signer;
                                                      }
                                                  }
                                                  function _hasCode(address addr) internal view returns (bool) {
                                                      uint256 size;
                                                      /// @solidity memory-safe-assembly
                                                      assembly { size := extcodesize(addr) }
                                                      return size > 0;
                                                  }
                                              }
                                              pragma solidity ^0.4.24;
                                              /**
                                               * @title ERC20 interface
                                               * @dev see https://github.com/ethereum/EIPs/issues/20
                                               */
                                              interface IERC20 {
                                                function totalSupply() external view returns (uint256);
                                                function balanceOf(address who) external view returns (uint256);
                                                function allowance(address owner, address spender)
                                                  external view returns (uint256);
                                                function transfer(address to, uint256 value) external returns (bool);
                                                function approve(address spender, uint256 value)
                                                  external returns (bool);
                                                function transferFrom(address from, address to, uint256 value)
                                                  external returns (bool);
                                                event Transfer(
                                                  address indexed from,
                                                  address indexed to,
                                                  uint256 value
                                                );
                                                event Approval(
                                                  address indexed owner,
                                                  address indexed spender,
                                                  uint256 value
                                                );
                                              }