ETH Price: $2,043.86 (+1.66%)

Transaction Decoder

Block:
19063443 at Jan-22-2024 04:26:59 PM +UTC
Transaction Fee:
0.009594500851323059 ETH $19.61
Gas Used:
475,009 Gas / 20.198566451 Gwei

Emitted Events:

133 EntryPoint.BeforeExecution( )
134 AppProxyUpgradeable.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x000000000000000000000000bbd6a5282f23b13eea7d6baf4c59fee37c4b78b6, 0x0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a5, 00000000000000000000000000000000000000000000000001755c412ec92fa7 )
135 AppProxyUpgradeable.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x000000000000000000000000bbd6a5282f23b13eea7d6baf4c59fee37c4b78b6, 0x0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a5, 0000000000000000000000000000000000000000000000000000000000000000 )
136 AppProxyUpgradeable.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000bbd6a5282f23b13eea7d6baf4c59fee37c4b78b6, 0x0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a5, 00000000000000000000000000000000000000000000000001755c412ec92fa7 )
137 AppProxyUpgradeable.0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb( 0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb, 0x000000000000000000000000bbd6a5282f23b13eea7d6baf4c59fee37c4b78b6, 0x0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a5, 0000000000000000000000000000000000000000000000000143605036cf07ec )
138 AppProxyUpgradeable.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a5, 0x000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff, 00000000000000000000000000000000000000000000000001755c412ec92fa7 )
139 AppProxyUpgradeable.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a5, 0x000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff, 0000000000000000000000000000000000000000000000000000000000000000 )
140 AppProxyUpgradeable.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a5, 0x00000000000000000000000022f9dcf4647084d6c31b2765f6910cd85c178c18, 00000000000000000000000000000000000000000000000001755c412ec92fa7 )
141 AppProxyUpgradeable.0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb( 0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb, 0x0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a5, 0x00000000000000000000000022f9dcf4647084d6c31b2765f6910cd85c178c18, 0000000000000000000000000000000000000000000000000143605036cf07ec )
142 AppProxyUpgradeable.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x00000000000000000000000022f9dcf4647084d6c31b2765f6910cd85c178c18, 0x000000000000000000000000dc24316b9ae028f1497c275eb9192a3ea0f67022, ffffffffffffffffffffffffffffffffffffffffffffa426b05efef90e3c6522 )
143 AppProxyUpgradeable.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000022f9dcf4647084d6c31b2765f6910cd85c178c18, 0x000000000000000000000000dc24316b9ae028f1497c275eb9192a3ea0f67022, 00000000000000000000000000000000000000000000000001755c412ec92fa7 )
144 AppProxyUpgradeable.0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb( 0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb, 0x00000000000000000000000022f9dcf4647084d6c31b2765f6910cd85c178c18, 0x000000000000000000000000dc24316b9ae028f1497c275eb9192a3ea0f67022, 0000000000000000000000000000000000000000000000000143605036cf07ec )
145 Vyper_contract.TokenExchange( buyer=FlashWallet, sold_id=1, tokens_sold=105091601340641191, bought_id=0, tokens_bought=105034844222071814 )
146 WETH9.Deposit( dst=FlashWallet, wad=105034844222071814 )
147 FlashWallet.0xe59e71a14fe90157eedc866c4f8c767d3943d6b6b2e8cd64dddcc92ab4c55af8( 0xe59e71a14fe90157eedc866c4f8c767d3943d6b6b2e8cd64dddcc92ab4c55af8, 0000000000000000000000000000000143757276650000000000000000000000, 000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84, 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2, 00000000000000000000000000000000000000000000000001755c412ec92fa7, 000000000000000000000000000000000000000000000000017528a262e52406 )
148 WETH9.Withdrawal( src=FlashWallet, wad=105034844222071814 )
149 AppProxyUpgradeable.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000022f9dcf4647084d6c31b2765f6910cd85c178c18, 0x0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a5, 0000000000000000000000000000000000000000000000000000000000000001 )
150 AppProxyUpgradeable.0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb( 0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb, 0x00000000000000000000000022f9dcf4647084d6c31b2765f6910cd85c178c18, 0x0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a5, 0000000000000000000000000000000000000000000000000000000000000000 )
151 ZeroEx.0x0f6672f78a59ba8e5e5b5d38df3ebc67f3c792e2c9259b8d97d7f00dd78ba1b3( 0x0f6672f78a59ba8e5e5b5d38df3ebc67f3c792e2c9259b8d97d7f00dd78ba1b3, 0x0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a5, 000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84, 000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee, 00000000000000000000000000000000000000000000000001755c412ec92fa7, 000000000000000000000000000000000000000000000000017528a262e52406 )
152 SocketGateway.0xb346a959ba6c0f1c7ba5426b10fd84fe4064e392a0dfcf6609e9640a0dd260d3( 0xb346a959ba6c0f1c7ba5426b10fd84fe4064e392a0dfcf6609e9640a0dd260d3, 000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84, 000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee, 000000000000000000000000000000000000000000000000017528a262e52406, 00000000000000000000000000000000000000000000000001755c412ec92fa7, 861b086cbd3ddee2b0b12c8ce3b43e1c111ac87dcabda086e02f18095da12f20, 000000000000000000000000bbd6a5282f23b13eea7d6baf4c59fee37c4b78b6, 0000000000000000000000000000000000000000000000000000000000000928 )
153 EntryPoint.UserOperationEvent( userOpHash=ECB68DCBF30C5A1E3B53EF5B630349119558938E12D4B78A3CC001ADB8223D65, sender=0xbbd6a5282f23b13eea7d6baf4c59fee37c4b78b6, paymaster=VerifyingPaymaster, nonce=3, success=True, actualGasCost=11433055163958883, actualGasUsed=566033 )

Account State Difference:

  Address   Before After State Difference Code
3.76111688274819541 Eth3.76111741950836541 Eth0.00000053676017
0x20e9695f...d4F155074
(Bundler: 0x20e...074)
0.796797834823315012 Eth
Nonce: 1115
0.798636389135950836 Eth
Nonce: 1116
0.001838554312635824
0x5FF137D4...a026d2789
(Entry Point 0.6.0)
46.158655682264972689 Eth46.147222627101013806 Eth0.011433055163958883
0xae7ab965...312D7fE84
0xbbD6a528...37c4B78B6 0.005 Eth0.110034844222071814 Eth0.105034844222071814
0xDC24316b...Ea0f67022
(Lido: Curve Liquidity Farming Pool Contract)
45,932.246751886786519256 Eth45,932.141717042564447442 Eth0.105034844222071814
0xE93ECa65...5D7990770
(Stackup: ERC-4337 Paymaster 2)

Execution Trace

EntryPoint.handleOps( ops=, beneficiary=0x20e9695f25413f14e5807b530D0698bd4F155074 )
  • 0xbbd6a5282f23b13eea7d6baf4c59fee37c4b78b6.3a871cdd( )
    • 0x8253291a17d3beb95fadab2751d52b324d22ef2d.3a871cdd( )
      • Null: 0x000...001.ecb68dcb( )
      • Null: 0x000...001.fbecfc19( )
      • VerifyingPaymaster.validatePaymasterUserOp( userOp=[{name:sender, type:address, order:1, indexed:false, value:0xbbD6a5282f23B13eeA7D6baf4C59fee37c4B78B6, valueString:0xbbD6a5282f23B13eeA7D6baf4C59fee37c4B78B6}, {name:nonce, type:uint256, order:2, indexed:false, value:3, valueString:3}, {name:initCode, type:bytes, order:3, indexed:false, value:0x, valueString:0x}, {name:callData, type:bytes, order:4, indexed:false, value:0x940D3C600000000000000000000000008AE01FCF7C655655FF2C6EF907B8B4718AB4E17C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000008E48D80FF0A0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000089600AE7AB96520DE3A18E5E111B5EAAB095312D7FE8400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044095EA7B30000000000000000000000003A23F943181408EAC424116AF7B7790C94CB97A500000000000000000000000000000000000000000000000001755C412EC92FA7003A23F943181408EAC424116AF7B7790C94CB97A5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007A8000001927899F9ED000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE00000000000000000000000000000000000000000000000001755C412EC92FA7000000000000000000000000BBD6A5282F23B13EEA7D6BAF4C59FEE37C4B78B6000000000000000000000000000000000000000000000000000000000000092800000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000006A8415565B0000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE00000000000000000000000000000000000000000000000001755C412EC92FA700000000000000000000000000000000000000000000000001734AFDBC0027A100000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000003E0000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000002A000000000000000000000000000000000000000000000000001755C412EC92FA7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000014375727665000000000000000000000000000000000000000000000000000000000000000000000001755C412EC92FA700000000000000000000000000000000000000000000000001734AFDBC0027A100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000DC24316B9AE028F1497C275EB9192A3EA0F670223DF021240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000000000000000001C000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE840000000000000000000000000000000000000000000000000000000000000000869584CD0000000000000000000000001000000000000000000000000000000000000011000000000000000000000000000000006348AC026AD2792BBC0B3F72F4A723E90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, valueString:0x940D3C600000000000000000000000008AE01FCF7C655655FF2C6EF907B8B4718AB4E17C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000008E48D80FF0A0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000089600AE7AB96520DE3A18E5E111B5EAAB095312D7FE8400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044095EA7B30000000000000000000000003A23F943181408EAC424116AF7B7790C94CB97A500000000000000000000000000000000000000000000000001755C412EC92FA7003A23F943181408EAC424116AF7B7790C94CB97A5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007A8000001927899F9ED000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE00000000000000000000000000000000000000000000000001755C412EC92FA7000000000000000000000000BBD6A5282F23B13EEA7D6BAF4C59FEE37C4B78B6000000000000000000000000000000000000000000000000000000000000092800000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000006A8415565B0000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE00000000000000000000000000000000000000000000000001755C412EC92FA700000000000000000000000000000000000000000000000001734AFDBC0027A100000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000003E0000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000002A000000000000000000000000000000000000000000000000001755C412EC92FA7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000014375727665000000000000000000000000000000000000000000000000000000000000000000000001755C412EC92FA700000000000000000000000000000000000000000000000001734AFDBC0027A100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000DC24316B9AE028F1497C275EB9192A3EA0F670223DF021240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000000000000000001C000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE840000000000000000000000000000000000000000000000000000000000000000869584CD0000000000000000000000001000000000000000000000000000000000000011000000000000000000000000000000006348AC026AD2792BBC0B3F72F4A723E90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000}, {name:callGasLimit, type:uint256, order:5, indexed:false, value:498342, valueString:498342}, {name:verificationGasLimit, type:uint256, order:6, indexed:false, value:91551, valueString:91551}, {name:preVerificationGas, type:uint256, order:7, indexed:false, value:69245, valueString:69245}, {name:maxFeePerGas, type:uint256, order:8, indexed:false, value:43649267598, valueString:43649267598}, {name:maxPriorityFeePerGas, type:uint256, order:9, indexed:false, value:1130000, valueString:1130000}, {name:paymasterAndData, type:bytes, order:10, indexed:false, value:0xE93ECA6595FE94091DC1AF46AAC2A8B5D79907700000000000000000000000000000000000000000000000000000000065AEA556000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000AF73792C4F0246DC7B3F552A8914DF945C72F17D3AF0DFFCEBF80E0EB414AF055B2A44E35BCA0E7C7A390C05AAAF1466F563454D51260A382EB3288BF3EDC5F71B, valueString:0xE93ECA6595FE94091DC1AF46AAC2A8B5D79907700000000000000000000000000000000000000000000000000000000065AEA556000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000AF73792C4F0246DC7B3F552A8914DF945C72F17D3AF0DFFCEBF80E0EB414AF055B2A44E35BCA0E7C7A390C05AAAF1466F563454D51260A382EB3288BF3EDC5F71B}, {name:signature, type:bytes, order:11, indexed:false, value:0x095D65FF9EBC693E3B137BE3E6D341C4E14E1B9E3D84DDD6F54BC676C83C068A730BCC22627597F84C22ED5798E5FD2574423D536748A6DF15B76BD28405BADE1C, valueString:0x095D65FF9EBC693E3B137BE3E6D341C4E14E1B9E3D84DDD6F54BC676C83C068A730BCC22627597F84C22ED5798E5FD2574423D536748A6DF15B76BD28405BADE1C}], userOpHash=ECB68DCBF30C5A1E3B53EF5B630349119558938E12D4B78A3CC001ADB8223D65, maxCost=36763159141739520 ) => ( context=0x, validationData=2493240542564494604238651708959388802494826166403261792256 )
        • Null: 0x000...001.68de7a02( )
        • EntryPoint.innerHandleOp( callData=0x940D3C600000000000000000000000008AE01FCF7C655655FF2C6EF907B8B4718AB4E17C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000008E48D80FF0A0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000089600AE7AB96520DE3A18E5E111B5EAAB095312D7FE8400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044095EA7B30000000000000000000000003A23F943181408EAC424116AF7B7790C94CB97A500000000000000000000000000000000000000000000000001755C412EC92FA7003A23F943181408EAC424116AF7B7790C94CB97A5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007A8000001927899F9ED000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE00000000000000000000000000000000000000000000000001755C412EC92FA7000000000000000000000000BBD6A5282F23B13EEA7D6BAF4C59FEE37C4B78B6000000000000000000000000000000000000000000000000000000000000092800000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000006A8415565B0000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE00000000000000000000000000000000000000000000000001755C412EC92FA700000000000000000000000000000000000000000000000001734AFDBC0027A100000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000003E0000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000002A000000000000000000000000000000000000000000000000001755C412EC92FA7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000014375727665000000000000000000000000000000000000000000000000000000000000000000000001755C412EC92FA700000000000000000000000000000000000000000000000001734AFDBC0027A100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000DC24316B9AE028F1497C275EB9192A3EA0F670223DF021240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000000000000000001C000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE840000000000000000000000000000000000000000000000000000000000000000869584CD0000000000000000000000001000000000000000000000000000000000000011000000000000000000000000000000006348AC026AD2792BBC0B3F72F4A723E90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, opInfo=[{name:mUserOp, type:tuple, order:1, indexed:false, value:[{name:sender, type:address, order:1, indexed:false, value:0xbbD6a5282f23B13eeA7D6baf4C59fee37c4B78B6, valueString:0xbbD6a5282f23B13eeA7D6baf4C59fee37c4B78B6}, {name:nonce, type:uint256, order:2, indexed:false, value:3, valueString:3}, {name:callGasLimit, type:uint256, order:3, indexed:false, value:498342, valueString:498342}, {name:verificationGasLimit, type:uint256, order:4, indexed:false, value:91551, valueString:91551}, {name:preVerificationGas, type:uint256, order:5, indexed:false, value:69245, valueString:69245}, {name:paymaster, type:address, order:6, indexed:false, value:0xE93ECa6595fe94091DC1af46aaC2A8b5D7990770, valueString:0xE93ECa6595fe94091DC1af46aaC2A8b5D7990770}, {name:maxFeePerGas, type:uint256, order:7, indexed:false, value:43649267598, valueString:43649267598}, {name:maxPriorityFeePerGas, type:uint256, order:8, indexed:false, value:1130000, valueString:1130000}], valueString:[{name:sender, type:address, order:1, indexed:false, value:0xbbD6a5282f23B13eeA7D6baf4C59fee37c4B78B6, valueString:0xbbD6a5282f23B13eeA7D6baf4C59fee37c4B78B6}, {name:nonce, type:uint256, order:2, indexed:false, value:3, valueString:3}, {name:callGasLimit, type:uint256, order:3, indexed:false, value:498342, valueString:498342}, {name:verificationGasLimit, type:uint256, order:4, indexed:false, value:91551, valueString:91551}, {name:preVerificationGas, type:uint256, order:5, indexed:false, value:69245, valueString:69245}, {name:paymaster, type:address, order:6, indexed:false, value:0xE93ECa6595fe94091DC1af46aaC2A8b5D7990770, valueString:0xE93ECa6595fe94091DC1af46aaC2A8b5D7990770}, {name:maxFeePerGas, type:uint256, order:7, indexed:false, value:43649267598, valueString:43649267598}, {name:maxPriorityFeePerGas, type:uint256, order:8, indexed:false, value:1130000, valueString:1130000}]}, {name:userOpHash, type:bytes32, order:2, indexed:false, value:ECB68DCBF30C5A1E3B53EF5B630349119558938E12D4B78A3CC001ADB8223D65, valueString:ECB68DCBF30C5A1E3B53EF5B630349119558938E12D4B78A3CC001ADB8223D65}, {name:prefund, type:uint256, order:3, indexed:false, value:36763159141739520, valueString:36763159141739520}, {name:contextOffset, type:uint256, order:4, indexed:false, value:1216, valueString:1216}, {name:preOpGas, type:uint256, order:5, indexed:false, value:135650, valueString:135650}], context=0x ) => ( actualGasCost=11433055163958883 )
          • 0xbbd6a5282f23b13eea7d6baf4c59fee37c4b78b6.940d3c60( )
            • 0x8253291a17d3beb95fadab2751d52b324d22ef2d.940d3c60( )
              • MultiSend.multiSend( transactions=0x00AE7AB96520DE3A18E5E111B5EAAB095312D7FE8400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044095EA7B30000000000000000000000003A23F943181408EAC424116AF7B7790C94CB97A500000000000000000000000000000000000000000000000001755C412EC92FA7003A23F943181408EAC424116AF7B7790C94CB97A5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007A8000001927899F9ED000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE00000000000000000000000000000000000000000000000001755C412EC92FA7000000000000000000000000BBD6A5282F23B13EEA7D6BAF4C59FEE37C4B78B6000000000000000000000000000000000000000000000000000000000000092800000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000006A8415565B0000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE00000000000000000000000000000000000000000000000001755C412EC92FA700000000000000000000000000000000000000000000000001734AFDBC0027A100000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000003E0000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000002A000000000000000000000000000000000000000000000000001755C412EC92FA7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000014375727665000000000000000000000000000000000000000000000000000000000000000000000001755C412EC92FA700000000000000000000000000000000000000000000000001734AFDBC0027A100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000DC24316B9AE028F1497C275EB9192A3EA0F670223DF021240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000000000000000001C000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE840000000000000000000000000000000000000000000000000000000000000000869584CD0000000000000000000000001000000000000000000000000000000000000011000000000000000000000000000000006348AC026AD2792BBC0B3F72F4A723E9000000000000000000000000000000000000000000000000 )
                • AppProxyUpgradeable.095ea7b3( )
                  • KernelProxy.be00bbd8( )
                  • Lido.approve( _spender=0x3a23F943181408EAC424116Af7b7790c94Cb97a5, _amount=105091601340641191 ) => ( True )
                  • SocketGateway.00000192( )
                    • ZeroXSwapImpl.performAction( fromToken=0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84, toToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, amount=105091601340641191, receiverAddress=0xbbD6a5282f23B13eeA7D6baf4C59fee37c4B78B6, metadata=0000000000000000000000000000000000000000000000000000000000000928, swapExtraData=0x415565B0000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE00000000000000000000000000000000000000000000000001755C412EC92FA700000000000000000000000000000000000000000000000001734AFDBC0027A100000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000003E0000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE84000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000002A000000000000000000000000000000000000000000000000001755C412EC92FA7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000014375727665000000000000000000000000000000000000000000000000000000000000000000000001755C412EC92FA700000000000000000000000000000000000000000000000001734AFDBC0027A100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000DC24316B9AE028F1497C275EB9192A3EA0F670223DF021240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000000000000000001C000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000AE7AB96520DE3A18E5E111B5EAAB095312D7FE840000000000000000000000000000000000000000000000000000000000000000869584CD0000000000000000000000001000000000000000000000000000000000000011000000000000000000000000000000006348AC026AD2792BBC0B3F72F4A723E9 ) => ( 105034844222071814 )
                    • ETH 0.011433055163958883 Bundler: 0x20e...074.CALL( )
                      handleOps[EntryPoint (ln:137)]
                      File 1 of 12: EntryPoint
                      // SPDX-License-Identifier: MIT
                      // OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)
                      pragma solidity ^0.8.0;
                      /**
                       * @dev Contract module that helps prevent reentrant calls to a function.
                       *
                       * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
                       * available, which can be applied to functions to make sure there are no nested
                       * (reentrant) calls to them.
                       *
                       * Note that because there is a single `nonReentrant` guard, functions marked as
                       * `nonReentrant` may not call one another. This can be worked around by making
                       * those functions `private`, and then adding `external` `nonReentrant` entry
                       * points to them.
                       *
                       * TIP: If you would like to learn more about reentrancy and alternative ways
                       * to protect against it, check out our blog post
                       * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
                       */
                      abstract contract ReentrancyGuard {
                          // Booleans are more expensive than uint256 or any type that takes up a full
                          // word because each write operation emits an extra SLOAD to first read the
                          // slot's contents, replace the bits taken up by the boolean, and then write
                          // back. This is the compiler's defense against contract upgrades and
                          // pointer aliasing, and it cannot be disabled.
                          // The values being non-zero value makes deployment a bit more expensive,
                          // but in exchange the refund on every call to nonReentrant will be lower in
                          // amount. Since refunds are capped to a percentage of the total
                          // transaction's gas, it is best to keep them low in cases like this one, to
                          // increase the likelihood of the full refund coming into effect.
                          uint256 private constant _NOT_ENTERED = 1;
                          uint256 private constant _ENTERED = 2;
                          uint256 private _status;
                          constructor() {
                              _status = _NOT_ENTERED;
                          }
                          /**
                           * @dev Prevents a contract from calling itself, directly or indirectly.
                           * Calling a `nonReentrant` function from another `nonReentrant`
                           * function is not supported. It is possible to prevent this from happening
                           * by making the `nonReentrant` function external, and making it call a
                           * `private` function that does the actual work.
                           */
                          modifier nonReentrant() {
                              _nonReentrantBefore();
                              _;
                              _nonReentrantAfter();
                          }
                          function _nonReentrantBefore() private {
                              // On the first call to nonReentrant, _status will be _NOT_ENTERED
                              require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                              // Any calls to nonReentrant after this point will fail
                              _status = _ENTERED;
                          }
                          function _nonReentrantAfter() private {
                              // By storing the original value once again, a refund is triggered (see
                              // https://eips.ethereum.org/EIPS/eip-2200)
                              _status = _NOT_ENTERED;
                          }
                      }
                      /**
                       ** Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
                       ** Only one instance required on each chain.
                       **/
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      /* solhint-disable avoid-low-level-calls */
                      /* solhint-disable no-inline-assembly */
                      import "../interfaces/IAccount.sol";
                      import "../interfaces/IPaymaster.sol";
                      import "../interfaces/IEntryPoint.sol";
                      import "../utils/Exec.sol";
                      import "./StakeManager.sol";
                      import "./SenderCreator.sol";
                      import "./Helpers.sol";
                      import "./NonceManager.sol";
                      import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
                      contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard {
                          using UserOperationLib for UserOperation;
                          SenderCreator private immutable senderCreator = new SenderCreator();
                          // internal value used during simulation: need to query aggregator.
                          address private constant SIMULATE_FIND_AGGREGATOR = address(1);
                          // marker for inner call revert on out of gas
                          bytes32 private constant INNER_OUT_OF_GAS = hex'deaddead';
                          uint256 private constant REVERT_REASON_MAX_LEN = 2048;
                          /**
                           * for simulation purposes, validateUserOp (and validatePaymasterUserOp) must return this value
                           * in case of signature failure, instead of revert.
                           */
                          uint256 public constant SIG_VALIDATION_FAILED = 1;
                          /**
                           * compensate the caller's beneficiary address with the collected fees of all UserOperations.
                           * @param beneficiary the address to receive the fees
                           * @param amount amount to transfer.
                           */
                          function _compensate(address payable beneficiary, uint256 amount) internal {
                              require(beneficiary != address(0), "AA90 invalid beneficiary");
                              (bool success,) = beneficiary.call{value : amount}("");
                              require(success, "AA91 failed send to beneficiary");
                          }
                          /**
                           * execute a user op
                           * @param opIndex index into the opInfo array
                           * @param userOp the userOp to execute
                           * @param opInfo the opInfo filled by validatePrepayment for this userOp.
                           * @return collected the total amount this userOp paid.
                           */
                          function _executeUserOp(uint256 opIndex, UserOperation calldata userOp, UserOpInfo memory opInfo) private returns (uint256 collected) {
                              uint256 preGas = gasleft();
                              bytes memory context = getMemoryBytesFromOffset(opInfo.contextOffset);
                              try this.innerHandleOp(userOp.callData, opInfo, context) returns (uint256 _actualGasCost) {
                                  collected = _actualGasCost;
                              } catch {
                                  bytes32 innerRevertCode;
                                  assembly {
                                      returndatacopy(0, 0, 32)
                                      innerRevertCode := mload(0)
                                  }
                                  // handleOps was called with gas limit too low. abort entire bundle.
                                  if (innerRevertCode == INNER_OUT_OF_GAS) {
                                      //report paymaster, since if it is not deliberately caused by the bundler,
                                      // it must be a revert caused by paymaster.
                                      revert FailedOp(opIndex, "AA95 out of gas");
                                  }
                                  uint256 actualGas = preGas - gasleft() + opInfo.preOpGas;
                                  collected = _handlePostOp(opIndex, IPaymaster.PostOpMode.postOpReverted, opInfo, context, actualGas);
                              }
                          }
                          /**
                           * Execute a batch of UserOperations.
                           * no signature aggregator is used.
                           * if any account requires an aggregator (that is, it returned an aggregator when
                           * performing simulateValidation), then handleAggregatedOps() must be used instead.
                           * @param ops the operations to execute
                           * @param beneficiary the address to receive the fees
                           */
                          function handleOps(UserOperation[] calldata ops, address payable beneficiary) public nonReentrant {
                              uint256 opslen = ops.length;
                              UserOpInfo[] memory opInfos = new UserOpInfo[](opslen);
                          unchecked {
                              for (uint256 i = 0; i < opslen; i++) {
                                  UserOpInfo memory opInfo = opInfos[i];
                                  (uint256 validationData, uint256 pmValidationData) = _validatePrepayment(i, ops[i], opInfo);
                                  _validateAccountAndPaymasterValidationData(i, validationData, pmValidationData, address(0));
                              }
                              uint256 collected = 0;
                              emit BeforeExecution();
                              for (uint256 i = 0; i < opslen; i++) {
                                  collected += _executeUserOp(i, ops[i], opInfos[i]);
                              }
                              _compensate(beneficiary, collected);
                          } //unchecked
                          }
                          /**
                           * Execute a batch of UserOperation with Aggregators
                           * @param opsPerAggregator the operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts)
                           * @param beneficiary the address to receive the fees
                           */
                          function handleAggregatedOps(
                              UserOpsPerAggregator[] calldata opsPerAggregator,
                              address payable beneficiary
                          ) public nonReentrant {
                              uint256 opasLen = opsPerAggregator.length;
                              uint256 totalOps = 0;
                              for (uint256 i = 0; i < opasLen; i++) {
                                  UserOpsPerAggregator calldata opa = opsPerAggregator[i];
                                  UserOperation[] calldata ops = opa.userOps;
                                  IAggregator aggregator = opa.aggregator;
                                  //address(1) is special marker of "signature error"
                                  require(address(aggregator) != address(1), "AA96 invalid aggregator");
                                  if (address(aggregator) != address(0)) {
                                      // solhint-disable-next-line no-empty-blocks
                                      try aggregator.validateSignatures(ops, opa.signature) {}
                                      catch {
                                          revert SignatureValidationFailed(address(aggregator));
                                      }
                                  }
                                  totalOps += ops.length;
                              }
                              UserOpInfo[] memory opInfos = new UserOpInfo[](totalOps);
                              emit BeforeExecution();
                              uint256 opIndex = 0;
                              for (uint256 a = 0; a < opasLen; a++) {
                                  UserOpsPerAggregator calldata opa = opsPerAggregator[a];
                                  UserOperation[] calldata ops = opa.userOps;
                                  IAggregator aggregator = opa.aggregator;
                                  uint256 opslen = ops.length;
                                  for (uint256 i = 0; i < opslen; i++) {
                                      UserOpInfo memory opInfo = opInfos[opIndex];
                                      (uint256 validationData, uint256 paymasterValidationData) = _validatePrepayment(opIndex, ops[i], opInfo);
                                      _validateAccountAndPaymasterValidationData(i, validationData, paymasterValidationData, address(aggregator));
                                      opIndex++;
                                  }
                              }
                              uint256 collected = 0;
                              opIndex = 0;
                              for (uint256 a = 0; a < opasLen; a++) {
                                  UserOpsPerAggregator calldata opa = opsPerAggregator[a];
                                  emit SignatureAggregatorChanged(address(opa.aggregator));
                                  UserOperation[] calldata ops = opa.userOps;
                                  uint256 opslen = ops.length;
                                  for (uint256 i = 0; i < opslen; i++) {
                                      collected += _executeUserOp(opIndex, ops[i], opInfos[opIndex]);
                                      opIndex++;
                                  }
                              }
                              emit SignatureAggregatorChanged(address(0));
                              _compensate(beneficiary, collected);
                          }
                          /// @inheritdoc IEntryPoint
                          function simulateHandleOp(UserOperation calldata op, address target, bytes calldata targetCallData) external override {
                              UserOpInfo memory opInfo;
                              _simulationOnlyValidations(op);
                              (uint256 validationData, uint256 paymasterValidationData) = _validatePrepayment(0, op, opInfo);
                              ValidationData memory data = _intersectTimeRange(validationData, paymasterValidationData);
                              numberMarker();
                              uint256 paid = _executeUserOp(0, op, opInfo);
                              numberMarker();
                              bool targetSuccess;
                              bytes memory targetResult;
                              if (target != address(0)) {
                                  (targetSuccess, targetResult) = target.call(targetCallData);
                              }
                              revert ExecutionResult(opInfo.preOpGas, paid, data.validAfter, data.validUntil, targetSuccess, targetResult);
                          }
                          // A memory copy of UserOp static fields only.
                          // Excluding: callData, initCode and signature. Replacing paymasterAndData with paymaster.
                          struct MemoryUserOp {
                              address sender;
                              uint256 nonce;
                              uint256 callGasLimit;
                              uint256 verificationGasLimit;
                              uint256 preVerificationGas;
                              address paymaster;
                              uint256 maxFeePerGas;
                              uint256 maxPriorityFeePerGas;
                          }
                          struct UserOpInfo {
                              MemoryUserOp mUserOp;
                              bytes32 userOpHash;
                              uint256 prefund;
                              uint256 contextOffset;
                              uint256 preOpGas;
                          }
                          /**
                           * inner function to handle a UserOperation.
                           * Must be declared "external" to open a call context, but it can only be called by handleOps.
                           */
                          function innerHandleOp(bytes memory callData, UserOpInfo memory opInfo, bytes calldata context) external returns (uint256 actualGasCost) {
                              uint256 preGas = gasleft();
                              require(msg.sender == address(this), "AA92 internal call only");
                              MemoryUserOp memory mUserOp = opInfo.mUserOp;
                              uint callGasLimit = mUserOp.callGasLimit;
                          unchecked {
                              // handleOps was called with gas limit too low. abort entire bundle.
                              if (gasleft() < callGasLimit + mUserOp.verificationGasLimit + 5000) {
                                  assembly {
                                      mstore(0, INNER_OUT_OF_GAS)
                                      revert(0, 32)
                                  }
                              }
                          }
                              IPaymaster.PostOpMode mode = IPaymaster.PostOpMode.opSucceeded;
                              if (callData.length > 0) {
                                  bool success = Exec.call(mUserOp.sender, 0, callData, callGasLimit);
                                  if (!success) {
                                      bytes memory result = Exec.getReturnData(REVERT_REASON_MAX_LEN);
                                      if (result.length > 0) {
                                          emit UserOperationRevertReason(opInfo.userOpHash, mUserOp.sender, mUserOp.nonce, result);
                                      }
                                      mode = IPaymaster.PostOpMode.opReverted;
                                  }
                              }
                          unchecked {
                              uint256 actualGas = preGas - gasleft() + opInfo.preOpGas;
                              //note: opIndex is ignored (relevant only if mode==postOpReverted, which is only possible outside of innerHandleOp)
                              return _handlePostOp(0, mode, opInfo, context, actualGas);
                          }
                          }
                          /**
                           * generate a request Id - unique identifier for this request.
                           * the request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid.
                           */
                          function getUserOpHash(UserOperation calldata userOp) public view returns (bytes32) {
                              return keccak256(abi.encode(userOp.hash(), address(this), block.chainid));
                          }
                          /**
                           * copy general fields from userOp into the memory opInfo structure.
                           */
                          function _copyUserOpToMemory(UserOperation calldata userOp, MemoryUserOp memory mUserOp) internal pure {
                              mUserOp.sender = userOp.sender;
                              mUserOp.nonce = userOp.nonce;
                              mUserOp.callGasLimit = userOp.callGasLimit;
                              mUserOp.verificationGasLimit = userOp.verificationGasLimit;
                              mUserOp.preVerificationGas = userOp.preVerificationGas;
                              mUserOp.maxFeePerGas = userOp.maxFeePerGas;
                              mUserOp.maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                              bytes calldata paymasterAndData = userOp.paymasterAndData;
                              if (paymasterAndData.length > 0) {
                                  require(paymasterAndData.length >= 20, "AA93 invalid paymasterAndData");
                                  mUserOp.paymaster = address(bytes20(paymasterAndData[: 20]));
                              } else {
                                  mUserOp.paymaster = address(0);
                              }
                          }
                          /**
                           * Simulate a call to account.validateUserOp and paymaster.validatePaymasterUserOp.
                           * @dev this method always revert. Successful result is ValidationResult error. other errors are failures.
                           * @dev The node must also verify it doesn't use banned opcodes, and that it doesn't reference storage outside the account's data.
                           * @param userOp the user operation to validate.
                           */
                          function simulateValidation(UserOperation calldata userOp) external {
                              UserOpInfo memory outOpInfo;
                              _simulationOnlyValidations(userOp);
                              (uint256 validationData, uint256 paymasterValidationData) = _validatePrepayment(0, userOp, outOpInfo);
                              StakeInfo memory paymasterInfo = _getStakeInfo(outOpInfo.mUserOp.paymaster);
                              StakeInfo memory senderInfo = _getStakeInfo(outOpInfo.mUserOp.sender);
                              StakeInfo memory factoryInfo;
                              {
                                  bytes calldata initCode = userOp.initCode;
                                  address factory = initCode.length >= 20 ? address(bytes20(initCode[0 : 20])) : address(0);
                                  factoryInfo = _getStakeInfo(factory);
                              }
                              ValidationData memory data = _intersectTimeRange(validationData, paymasterValidationData);
                              address aggregator = data.aggregator;
                              bool sigFailed = aggregator == address(1);
                              ReturnInfo memory returnInfo = ReturnInfo(outOpInfo.preOpGas, outOpInfo.prefund,
                                  sigFailed, data.validAfter, data.validUntil, getMemoryBytesFromOffset(outOpInfo.contextOffset));
                              if (aggregator != address(0) && aggregator != address(1)) {
                                  AggregatorStakeInfo memory aggregatorInfo = AggregatorStakeInfo(aggregator, _getStakeInfo(aggregator));
                                  revert ValidationResultWithAggregation(returnInfo, senderInfo, factoryInfo, paymasterInfo, aggregatorInfo);
                              }
                              revert ValidationResult(returnInfo, senderInfo, factoryInfo, paymasterInfo);
                          }
                          function _getRequiredPrefund(MemoryUserOp memory mUserOp) internal pure returns (uint256 requiredPrefund) {
                          unchecked {
                              //when using a Paymaster, the verificationGasLimit is used also to as a limit for the postOp call.
                              // our security model might call postOp eventually twice
                              uint256 mul = mUserOp.paymaster != address(0) ? 3 : 1;
                              uint256 requiredGas = mUserOp.callGasLimit + mUserOp.verificationGasLimit * mul + mUserOp.preVerificationGas;
                              requiredPrefund = requiredGas * mUserOp.maxFeePerGas;
                          }
                          }
                          // create the sender's contract if needed.
                          function _createSenderIfNeeded(uint256 opIndex, UserOpInfo memory opInfo, bytes calldata initCode) internal {
                              if (initCode.length != 0) {
                                  address sender = opInfo.mUserOp.sender;
                                  if (sender.code.length != 0) revert FailedOp(opIndex, "AA10 sender already constructed");
                                  address sender1 = senderCreator.createSender{gas : opInfo.mUserOp.verificationGasLimit}(initCode);
                                  if (sender1 == address(0)) revert FailedOp(opIndex, "AA13 initCode failed or OOG");
                                  if (sender1 != sender) revert FailedOp(opIndex, "AA14 initCode must return sender");
                                  if (sender1.code.length == 0) revert FailedOp(opIndex, "AA15 initCode must create sender");
                                  address factory = address(bytes20(initCode[0 : 20]));
                                  emit AccountDeployed(opInfo.userOpHash, sender, factory, opInfo.mUserOp.paymaster);
                              }
                          }
                          /**
                           * Get counterfactual sender address.
                           *  Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation.
                           * this method always revert, and returns the address in SenderAddressResult error
                           * @param initCode the constructor code to be passed into the UserOperation.
                           */
                          function getSenderAddress(bytes calldata initCode) public {
                              address sender = senderCreator.createSender(initCode);
                              revert SenderAddressResult(sender);
                          }
                          function _simulationOnlyValidations(UserOperation calldata userOp) internal view {
                              // solhint-disable-next-line no-empty-blocks
                              try this._validateSenderAndPaymaster(userOp.initCode, userOp.sender, userOp.paymasterAndData) {}
                              catch Error(string memory revertReason) {
                                  if (bytes(revertReason).length != 0) {
                                      revert FailedOp(0, revertReason);
                                  }
                              }
                          }
                          /**
                          * Called only during simulation.
                          * This function always reverts to prevent warm/cold storage differentiation in simulation vs execution.
                          */
                          function _validateSenderAndPaymaster(bytes calldata initCode, address sender, bytes calldata paymasterAndData) external view {
                              if (initCode.length == 0 && sender.code.length == 0) {
                                  // it would revert anyway. but give a meaningful message
                                  revert("AA20 account not deployed");
                              }
                              if (paymasterAndData.length >= 20) {
                                  address paymaster = address(bytes20(paymasterAndData[0 : 20]));
                                  if (paymaster.code.length == 0) {
                                      // it would revert anyway. but give a meaningful message
                                      revert("AA30 paymaster not deployed");
                                  }
                              }
                              // always revert
                              revert("");
                          }
                          /**
                           * call account.validateUserOp.
                           * revert (with FailedOp) in case validateUserOp reverts, or account didn't send required prefund.
                           * decrement account's deposit if needed
                           */
                          function _validateAccountPrepayment(uint256 opIndex, UserOperation calldata op, UserOpInfo memory opInfo, uint256 requiredPrefund)
                          internal returns (uint256 gasUsedByValidateAccountPrepayment, uint256 validationData) {
                          unchecked {
                              uint256 preGas = gasleft();
                              MemoryUserOp memory mUserOp = opInfo.mUserOp;
                              address sender = mUserOp.sender;
                              _createSenderIfNeeded(opIndex, opInfo, op.initCode);
                              address paymaster = mUserOp.paymaster;
                              numberMarker();
                              uint256 missingAccountFunds = 0;
                              if (paymaster == address(0)) {
                                  uint256 bal = balanceOf(sender);
                                  missingAccountFunds = bal > requiredPrefund ? 0 : requiredPrefund - bal;
                              }
                              try IAccount(sender).validateUserOp{gas : mUserOp.verificationGasLimit}(op, opInfo.userOpHash, missingAccountFunds)
                              returns (uint256 _validationData) {
                                  validationData = _validationData;
                              } catch Error(string memory revertReason) {
                                  revert FailedOp(opIndex, string.concat("AA23 reverted: ", revertReason));
                              } catch {
                                  revert FailedOp(opIndex, "AA23 reverted (or OOG)");
                              }
                              if (paymaster == address(0)) {
                                  DepositInfo storage senderInfo = deposits[sender];
                                  uint256 deposit = senderInfo.deposit;
                                  if (requiredPrefund > deposit) {
                                      revert FailedOp(opIndex, "AA21 didn't pay prefund");
                                  }
                                  senderInfo.deposit = uint112(deposit - requiredPrefund);
                              }
                              gasUsedByValidateAccountPrepayment = preGas - gasleft();
                          }
                          }
                          /**
                           * In case the request has a paymaster:
                           * Validate paymaster has enough deposit.
                           * Call paymaster.validatePaymasterUserOp.
                           * Revert with proper FailedOp in case paymaster reverts.
                           * Decrement paymaster's deposit
                           */
                          function _validatePaymasterPrepayment(uint256 opIndex, UserOperation calldata op, UserOpInfo memory opInfo, uint256 requiredPreFund, uint256 gasUsedByValidateAccountPrepayment)
                          internal returns (bytes memory context, uint256 validationData) {
                          unchecked {
                              MemoryUserOp memory mUserOp = opInfo.mUserOp;
                              uint256 verificationGasLimit = mUserOp.verificationGasLimit;
                              require(verificationGasLimit > gasUsedByValidateAccountPrepayment, "AA41 too little verificationGas");
                              uint256 gas = verificationGasLimit - gasUsedByValidateAccountPrepayment;
                              address paymaster = mUserOp.paymaster;
                              DepositInfo storage paymasterInfo = deposits[paymaster];
                              uint256 deposit = paymasterInfo.deposit;
                              if (deposit < requiredPreFund) {
                                  revert FailedOp(opIndex, "AA31 paymaster deposit too low");
                              }
                              paymasterInfo.deposit = uint112(deposit - requiredPreFund);
                              try IPaymaster(paymaster).validatePaymasterUserOp{gas : gas}(op, opInfo.userOpHash, requiredPreFund) returns (bytes memory _context, uint256 _validationData){
                                  context = _context;
                                  validationData = _validationData;
                              } catch Error(string memory revertReason) {
                                  revert FailedOp(opIndex, string.concat("AA33 reverted: ", revertReason));
                              } catch {
                                  revert FailedOp(opIndex, "AA33 reverted (or OOG)");
                              }
                          }
                          }
                          /**
                           * revert if either account validationData or paymaster validationData is expired
                           */
                          function _validateAccountAndPaymasterValidationData(uint256 opIndex, uint256 validationData, uint256 paymasterValidationData, address expectedAggregator) internal view {
                              (address aggregator, bool outOfTimeRange) = _getValidationData(validationData);
                              if (expectedAggregator != aggregator) {
                                  revert FailedOp(opIndex, "AA24 signature error");
                              }
                              if (outOfTimeRange) {
                                  revert FailedOp(opIndex, "AA22 expired or not due");
                              }
                              //pmAggregator is not a real signature aggregator: we don't have logic to handle it as address.
                              // non-zero address means that the paymaster fails due to some signature check (which is ok only during estimation)
                              address pmAggregator;
                              (pmAggregator, outOfTimeRange) = _getValidationData(paymasterValidationData);
                              if (pmAggregator != address(0)) {
                                  revert FailedOp(opIndex, "AA34 signature error");
                              }
                              if (outOfTimeRange) {
                                  revert FailedOp(opIndex, "AA32 paymaster expired or not due");
                              }
                          }
                          function _getValidationData(uint256 validationData) internal view returns (address aggregator, bool outOfTimeRange) {
                              if (validationData == 0) {
                                  return (address(0), false);
                              }
                              ValidationData memory data = _parseValidationData(validationData);
                              // solhint-disable-next-line not-rely-on-time
                              outOfTimeRange = block.timestamp > data.validUntil || block.timestamp < data.validAfter;
                              aggregator = data.aggregator;
                          }
                          /**
                           * validate account and paymaster (if defined).
                           * also make sure total validation doesn't exceed verificationGasLimit
                           * this method is called off-chain (simulateValidation()) and on-chain (from handleOps)
                           * @param opIndex the index of this userOp into the "opInfos" array
                           * @param userOp the userOp to validate
                           */
                          function _validatePrepayment(uint256 opIndex, UserOperation calldata userOp, UserOpInfo memory outOpInfo)
                          private returns (uint256 validationData, uint256 paymasterValidationData) {
                              uint256 preGas = gasleft();
                              MemoryUserOp memory mUserOp = outOpInfo.mUserOp;
                              _copyUserOpToMemory(userOp, mUserOp);
                              outOpInfo.userOpHash = getUserOpHash(userOp);
                              // validate all numeric values in userOp are well below 128 bit, so they can safely be added
                              // and multiplied without causing overflow
                              uint256 maxGasValues = mUserOp.preVerificationGas | mUserOp.verificationGasLimit | mUserOp.callGasLimit |
                              userOp.maxFeePerGas | userOp.maxPriorityFeePerGas;
                              require(maxGasValues <= type(uint120).max, "AA94 gas values overflow");
                              uint256 gasUsedByValidateAccountPrepayment;
                              (uint256 requiredPreFund) = _getRequiredPrefund(mUserOp);
                              (gasUsedByValidateAccountPrepayment, validationData) = _validateAccountPrepayment(opIndex, userOp, outOpInfo, requiredPreFund);
                              if (!_validateAndUpdateNonce(mUserOp.sender, mUserOp.nonce)) {
                                  revert FailedOp(opIndex, "AA25 invalid account nonce");
                              }
                              //a "marker" where account opcode validation is done and paymaster opcode validation is about to start
                              // (used only by off-chain simulateValidation)
                              numberMarker();
                              bytes memory context;
                              if (mUserOp.paymaster != address(0)) {
                                  (context, paymasterValidationData) = _validatePaymasterPrepayment(opIndex, userOp, outOpInfo, requiredPreFund, gasUsedByValidateAccountPrepayment);
                              }
                          unchecked {
                              uint256 gasUsed = preGas - gasleft();
                              if (userOp.verificationGasLimit < gasUsed) {
                                  revert FailedOp(opIndex, "AA40 over verificationGasLimit");
                              }
                              outOpInfo.prefund = requiredPreFund;
                              outOpInfo.contextOffset = getOffsetOfMemoryBytes(context);
                              outOpInfo.preOpGas = preGas - gasleft() + userOp.preVerificationGas;
                          }
                          }
                          /**
                           * process post-operation.
                           * called just after the callData is executed.
                           * if a paymaster is defined and its validation returned a non-empty context, its postOp is called.
                           * the excess amount is refunded to the account (or paymaster - if it was used in the request)
                           * @param opIndex index in the batch
                           * @param mode - whether is called from innerHandleOp, or outside (postOpReverted)
                           * @param opInfo userOp fields and info collected during validation
                           * @param context the context returned in validatePaymasterUserOp
                           * @param actualGas the gas used so far by this user operation
                           */
                          function _handlePostOp(uint256 opIndex, IPaymaster.PostOpMode mode, UserOpInfo memory opInfo, bytes memory context, uint256 actualGas) private returns (uint256 actualGasCost) {
                              uint256 preGas = gasleft();
                          unchecked {
                              address refundAddress;
                              MemoryUserOp memory mUserOp = opInfo.mUserOp;
                              uint256 gasPrice = getUserOpGasPrice(mUserOp);
                              address paymaster = mUserOp.paymaster;
                              if (paymaster == address(0)) {
                                  refundAddress = mUserOp.sender;
                              } else {
                                  refundAddress = paymaster;
                                  if (context.length > 0) {
                                      actualGasCost = actualGas * gasPrice;
                                      if (mode != IPaymaster.PostOpMode.postOpReverted) {
                                          IPaymaster(paymaster).postOp{gas : mUserOp.verificationGasLimit}(mode, context, actualGasCost);
                                      } else {
                                          // solhint-disable-next-line no-empty-blocks
                                          try IPaymaster(paymaster).postOp{gas : mUserOp.verificationGasLimit}(mode, context, actualGasCost) {}
                                          catch Error(string memory reason) {
                                              revert FailedOp(opIndex, string.concat("AA50 postOp reverted: ", reason));
                                          }
                                          catch {
                                              revert FailedOp(opIndex, "AA50 postOp revert");
                                          }
                                      }
                                  }
                              }
                              actualGas += preGas - gasleft();
                              actualGasCost = actualGas * gasPrice;
                              if (opInfo.prefund < actualGasCost) {
                                  revert FailedOp(opIndex, "AA51 prefund below actualGasCost");
                              }
                              uint256 refund = opInfo.prefund - actualGasCost;
                              _incrementDeposit(refundAddress, refund);
                              bool success = mode == IPaymaster.PostOpMode.opSucceeded;
                              emit UserOperationEvent(opInfo.userOpHash, mUserOp.sender, mUserOp.paymaster, mUserOp.nonce, success, actualGasCost, actualGas);
                          } // unchecked
                          }
                          /**
                           * the gas price this UserOp agrees to pay.
                           * relayer/block builder might submit the TX with higher priorityFee, but the user should not
                           */
                          function getUserOpGasPrice(MemoryUserOp memory mUserOp) internal view returns (uint256) {
                          unchecked {
                              uint256 maxFeePerGas = mUserOp.maxFeePerGas;
                              uint256 maxPriorityFeePerGas = mUserOp.maxPriorityFeePerGas;
                              if (maxFeePerGas == maxPriorityFeePerGas) {
                                  //legacy mode (for networks that don't support basefee opcode)
                                  return maxFeePerGas;
                              }
                              return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
                          }
                          }
                          function min(uint256 a, uint256 b) internal pure returns (uint256) {
                              return a < b ? a : b;
                          }
                          function getOffsetOfMemoryBytes(bytes memory data) internal pure returns (uint256 offset) {
                              assembly {offset := data}
                          }
                          function getMemoryBytesFromOffset(uint256 offset) internal pure returns (bytes memory data) {
                              assembly {data := offset}
                          }
                          //place the NUMBER opcode in the code.
                          // this is used as a marker during simulation, as this OP is completely banned from the simulated code of the
                          // account and paymaster.
                          function numberMarker() internal view {
                              assembly {mstore(0, number())}
                          }
                      }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      /* solhint-disable no-inline-assembly */
                      /**
                       * returned data from validateUserOp.
                       * validateUserOp returns a uint256, with is created by `_packedValidationData` and parsed by `_parseValidationData`
                       * @param aggregator - address(0) - the account validated the signature by itself.
                       *              address(1) - the account failed to validate the signature.
                       *              otherwise - this is an address of a signature aggregator that must be used to validate the signature.
                       * @param validAfter - this UserOp is valid only after this timestamp.
                       * @param validaUntil - this UserOp is valid only up to this timestamp.
                       */
                          struct ValidationData {
                              address aggregator;
                              uint48 validAfter;
                              uint48 validUntil;
                          }
                      //extract sigFailed, validAfter, validUntil.
                      // also convert zero validUntil to type(uint48).max
                          function _parseValidationData(uint validationData) pure returns (ValidationData memory data) {
                              address aggregator = address(uint160(validationData));
                              uint48 validUntil = uint48(validationData >> 160);
                              if (validUntil == 0) {
                                  validUntil = type(uint48).max;
                              }
                              uint48 validAfter = uint48(validationData >> (48 + 160));
                              return ValidationData(aggregator, validAfter, validUntil);
                          }
                      // intersect account and paymaster ranges.
                          function _intersectTimeRange(uint256 validationData, uint256 paymasterValidationData) pure returns (ValidationData memory) {
                              ValidationData memory accountValidationData = _parseValidationData(validationData);
                              ValidationData memory pmValidationData = _parseValidationData(paymasterValidationData);
                              address aggregator = accountValidationData.aggregator;
                              if (aggregator == address(0)) {
                                  aggregator = pmValidationData.aggregator;
                              }
                              uint48 validAfter = accountValidationData.validAfter;
                              uint48 validUntil = accountValidationData.validUntil;
                              uint48 pmValidAfter = pmValidationData.validAfter;
                              uint48 pmValidUntil = pmValidationData.validUntil;
                              if (validAfter < pmValidAfter) validAfter = pmValidAfter;
                              if (validUntil > pmValidUntil) validUntil = pmValidUntil;
                              return ValidationData(aggregator, validAfter, validUntil);
                          }
                      /**
                       * helper to pack the return value for validateUserOp
                       * @param data - the ValidationData to pack
                       */
                          function _packValidationData(ValidationData memory data) pure returns (uint256) {
                              return uint160(data.aggregator) | (uint256(data.validUntil) << 160) | (uint256(data.validAfter) << (160 + 48));
                          }
                      /**
                       * helper to pack the return value for validateUserOp, when not using an aggregator
                       * @param sigFailed - true for signature failure, false for success
                       * @param validUntil last timestamp this UserOperation is valid (or zero for infinite)
                       * @param validAfter first timestamp this UserOperation is valid
                       */
                          function _packValidationData(bool sigFailed, uint48 validUntil, uint48 validAfter) pure returns (uint256) {
                              return (sigFailed ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48));
                          }
                      /**
                       * keccak function over calldata.
                       * @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it.
                       */
                          function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) {
                              assembly {
                                  let mem := mload(0x40)
                                  let len := data.length
                                  calldatacopy(mem, data.offset, len)
                                  ret := keccak256(mem, len)
                              }
                          }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      import "../interfaces/IEntryPoint.sol";
                      /**
                       * nonce management functionality
                       */
                      contract NonceManager is INonceManager {
                          /**
                           * The next valid sequence number for a given nonce key.
                           */
                          mapping(address => mapping(uint192 => uint256)) public nonceSequenceNumber;
                          function getNonce(address sender, uint192 key)
                          public view override returns (uint256 nonce) {
                              return nonceSequenceNumber[sender][key] | (uint256(key) << 64);
                          }
                          // allow an account to manually increment its own nonce.
                          // (mainly so that during construction nonce can be made non-zero,
                          // to "absorb" the gas cost of first nonce increment to 1st transaction (construction),
                          // not to 2nd transaction)
                          function incrementNonce(uint192 key) public override {
                              nonceSequenceNumber[msg.sender][key]++;
                          }
                          /**
                           * validate nonce uniqueness for this account.
                           * called just after validateUserOp()
                           */
                          function _validateAndUpdateNonce(address sender, uint256 nonce) internal returns (bool) {
                              uint192 key = uint192(nonce >> 64);
                              uint64 seq = uint64(nonce);
                              return nonceSequenceNumber[sender][key]++ == seq;
                          }
                      }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      /**
                       * helper contract for EntryPoint, to call userOp.initCode from a "neutral" address,
                       * which is explicitly not the entryPoint itself.
                       */
                      contract SenderCreator {
                          /**
                           * call the "initCode" factory to create and return the sender account address
                           * @param initCode the initCode value from a UserOp. contains 20 bytes of factory address, followed by calldata
                           * @return sender the returned address of the created account, or zero address on failure.
                           */
                          function createSender(bytes calldata initCode) external returns (address sender) {
                              address factory = address(bytes20(initCode[0 : 20]));
                              bytes memory initCallData = initCode[20 :];
                              bool success;
                              /* solhint-disable no-inline-assembly */
                              assembly {
                                  success := call(gas(), factory, 0, add(initCallData, 0x20), mload(initCallData), 0, 32)
                                  sender := mload(0)
                              }
                              if (!success) {
                                  sender = address(0);
                              }
                          }
                      }
                      // SPDX-License-Identifier: GPL-3.0-only
                      pragma solidity ^0.8.12;
                      import "../interfaces/IStakeManager.sol";
                      /* solhint-disable avoid-low-level-calls */
                      /* solhint-disable not-rely-on-time */
                      /**
                       * manage deposits and stakes.
                       * deposit is just a balance used to pay for UserOperations (either by a paymaster or an account)
                       * stake is value locked for at least "unstakeDelay" by a paymaster.
                       */
                      abstract contract StakeManager is IStakeManager {
                          /// maps paymaster to their deposits and stakes
                          mapping(address => DepositInfo) public deposits;
                          /// @inheritdoc IStakeManager
                          function getDepositInfo(address account) public view returns (DepositInfo memory info) {
                              return deposits[account];
                          }
                          // internal method to return just the stake info
                          function _getStakeInfo(address addr) internal view returns (StakeInfo memory info) {
                              DepositInfo storage depositInfo = deposits[addr];
                              info.stake = depositInfo.stake;
                              info.unstakeDelaySec = depositInfo.unstakeDelaySec;
                          }
                          /// return the deposit (for gas payment) of the account
                          function balanceOf(address account) public view returns (uint256) {
                              return deposits[account].deposit;
                          }
                          receive() external payable {
                              depositTo(msg.sender);
                          }
                          function _incrementDeposit(address account, uint256 amount) internal {
                              DepositInfo storage info = deposits[account];
                              uint256 newAmount = info.deposit + amount;
                              require(newAmount <= type(uint112).max, "deposit overflow");
                              info.deposit = uint112(newAmount);
                          }
                          /**
                           * add to the deposit of the given account
                           */
                          function depositTo(address account) public payable {
                              _incrementDeposit(account, msg.value);
                              DepositInfo storage info = deposits[account];
                              emit Deposited(account, info.deposit);
                          }
                          /**
                           * add to the account's stake - amount and delay
                           * any pending unstake is first cancelled.
                           * @param unstakeDelaySec the new lock duration before the deposit can be withdrawn.
                           */
                          function addStake(uint32 unstakeDelaySec) public payable {
                              DepositInfo storage info = deposits[msg.sender];
                              require(unstakeDelaySec > 0, "must specify unstake delay");
                              require(unstakeDelaySec >= info.unstakeDelaySec, "cannot decrease unstake time");
                              uint256 stake = info.stake + msg.value;
                              require(stake > 0, "no stake specified");
                              require(stake <= type(uint112).max, "stake overflow");
                              deposits[msg.sender] = DepositInfo(
                                  info.deposit,
                                  true,
                                  uint112(stake),
                                  unstakeDelaySec,
                                  0
                              );
                              emit StakeLocked(msg.sender, stake, unstakeDelaySec);
                          }
                          /**
                           * attempt to unlock the stake.
                           * the value can be withdrawn (using withdrawStake) after the unstake delay.
                           */
                          function unlockStake() external {
                              DepositInfo storage info = deposits[msg.sender];
                              require(info.unstakeDelaySec != 0, "not staked");
                              require(info.staked, "already unstaking");
                              uint48 withdrawTime = uint48(block.timestamp) + info.unstakeDelaySec;
                              info.withdrawTime = withdrawTime;
                              info.staked = false;
                              emit StakeUnlocked(msg.sender, withdrawTime);
                          }
                          /**
                           * withdraw from the (unlocked) stake.
                           * must first call unlockStake and wait for the unstakeDelay to pass
                           * @param withdrawAddress the address to send withdrawn value.
                           */
                          function withdrawStake(address payable withdrawAddress) external {
                              DepositInfo storage info = deposits[msg.sender];
                              uint256 stake = info.stake;
                              require(stake > 0, "No stake to withdraw");
                              require(info.withdrawTime > 0, "must call unlockStake() first");
                              require(info.withdrawTime <= block.timestamp, "Stake withdrawal is not due");
                              info.unstakeDelaySec = 0;
                              info.withdrawTime = 0;
                              info.stake = 0;
                              emit StakeWithdrawn(msg.sender, withdrawAddress, stake);
                              (bool success,) = withdrawAddress.call{value : stake}("");
                              require(success, "failed to withdraw stake");
                          }
                          /**
                           * withdraw from the deposit.
                           * @param withdrawAddress the address to send withdrawn value.
                           * @param withdrawAmount the amount to withdraw.
                           */
                          function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external {
                              DepositInfo storage info = deposits[msg.sender];
                              require(withdrawAmount <= info.deposit, "Withdraw amount too large");
                              info.deposit = uint112(info.deposit - withdrawAmount);
                              emit Withdrawn(msg.sender, withdrawAddress, withdrawAmount);
                              (bool success,) = withdrawAddress.call{value : withdrawAmount}("");
                              require(success, "failed to withdraw");
                          }
                      }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      import "./UserOperation.sol";
                      interface IAccount {
                          /**
                           * Validate user's signature and nonce
                           * the entryPoint will make the call to the recipient only if this validation call returns successfully.
                           * signature failure should be reported by returning SIG_VALIDATION_FAILED (1).
                           * This allows making a "simulation call" without a valid signature
                           * Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure.
                           *
                           * @dev Must validate caller is the entryPoint.
                           *      Must validate the signature and nonce
                           * @param userOp the operation that is about to be executed.
                           * @param userOpHash hash of the user's request data. can be used as the basis for signature.
                           * @param missingAccountFunds missing funds on the account's deposit in the entrypoint.
                           *      This is the minimum amount to transfer to the sender(entryPoint) to be able to make the call.
                           *      The excess is left as a deposit in the entrypoint, for future calls.
                           *      can be withdrawn anytime using "entryPoint.withdrawTo()"
                           *      In case there is a paymaster in the request (or the current deposit is high enough), this value will be zero.
                           * @return validationData packaged ValidationData structure. use `_packValidationData` and `_unpackValidationData` to encode and decode
                           *      <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
                           *         otherwise, an address of an "authorizer" contract.
                           *      <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite"
                           *      <6-byte> validAfter - first timestamp this operation is valid
                           *      If an account doesn't use time-range, it is enough to return SIG_VALIDATION_FAILED value (1) for signature failure.
                           *      Note that the validation code cannot use block.timestamp (or block.number) directly.
                           */
                          function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
                          external returns (uint256 validationData);
                      }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      import "./UserOperation.sol";
                      /**
                       * Aggregated Signatures validator.
                       */
                      interface IAggregator {
                          /**
                           * validate aggregated signature.
                           * revert if the aggregated signature does not match the given list of operations.
                           */
                          function validateSignatures(UserOperation[] calldata userOps, bytes calldata signature) external view;
                          /**
                           * validate signature of a single userOp
                           * This method is should be called by bundler after EntryPoint.simulateValidation() returns (reverts) with ValidationResultWithAggregation
                           * First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps.
                           * @param userOp the userOperation received from the user.
                           * @return sigForUserOp the value to put into the signature field of the userOp when calling handleOps.
                           *    (usually empty, unless account and aggregator support some kind of "multisig"
                           */
                          function validateUserOpSignature(UserOperation calldata userOp)
                          external view returns (bytes memory sigForUserOp);
                          /**
                           * aggregate multiple signatures into a single value.
                           * This method is called off-chain to calculate the signature to pass with handleOps()
                           * bundler MAY use optimized custom code perform this aggregation
                           * @param userOps array of UserOperations to collect the signatures from.
                           * @return aggregatedSignature the aggregated signature
                           */
                          function aggregateSignatures(UserOperation[] calldata userOps) external view returns (bytes memory aggregatedSignature);
                      }
                      /**
                       ** Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
                       ** Only one instance required on each chain.
                       **/
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      /* solhint-disable avoid-low-level-calls */
                      /* solhint-disable no-inline-assembly */
                      /* solhint-disable reason-string */
                      import "./UserOperation.sol";
                      import "./IStakeManager.sol";
                      import "./IAggregator.sol";
                      import "./INonceManager.sol";
                      interface IEntryPoint is IStakeManager, INonceManager {
                          /***
                           * An event emitted after each successful request
                           * @param userOpHash - unique identifier for the request (hash its entire content, except signature).
                           * @param sender - the account that generates this request.
                           * @param paymaster - if non-null, the paymaster that pays for this request.
                           * @param nonce - the nonce value from the request.
                           * @param success - true if the sender transaction succeeded, false if reverted.
                           * @param actualGasCost - actual amount paid (by account or paymaster) for this UserOperation.
                           * @param actualGasUsed - total gas used by this UserOperation (including preVerification, creation, validation and execution).
                           */
                          event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed);
                          /**
                           * account "sender" was deployed.
                           * @param userOpHash the userOp that deployed this account. UserOperationEvent will follow.
                           * @param sender the account that is deployed
                           * @param factory the factory used to deploy this account (in the initCode)
                           * @param paymaster the paymaster used by this UserOp
                           */
                          event AccountDeployed(bytes32 indexed userOpHash, address indexed sender, address factory, address paymaster);
                          /**
                           * An event emitted if the UserOperation "callData" reverted with non-zero length
                           * @param userOpHash the request unique identifier.
                           * @param sender the sender of this request
                           * @param nonce the nonce used in the request
                           * @param revertReason - the return bytes from the (reverted) call to "callData".
                           */
                          event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason);
                          /**
                           * an event emitted by handleOps(), before starting the execution loop.
                           * any event emitted before this event, is part of the validation.
                           */
                          event BeforeExecution();
                          /**
                           * signature aggregator used by the following UserOperationEvents within this bundle.
                           */
                          event SignatureAggregatorChanged(address indexed aggregator);
                          /**
                           * a custom revert error of handleOps, to identify the offending op.
                           *  NOTE: if simulateValidation passes successfully, there should be no reason for handleOps to fail on it.
                           *  @param opIndex - index into the array of ops to the failed one (in simulateValidation, this is always zero)
                           *  @param reason - revert reason
                           *      The string starts with a unique code "AAmn", where "m" is "1" for factory, "2" for account and "3" for paymaster issues,
                           *      so a failure can be attributed to the correct entity.
                           *   Should be caught in off-chain handleOps simulation and not happen on-chain.
                           *   Useful for mitigating DoS attempts against batchers or for troubleshooting of factory/account/paymaster reverts.
                           */
                          error FailedOp(uint256 opIndex, string reason);
                          /**
                           * error case when a signature aggregator fails to verify the aggregated signature it had created.
                           */
                          error SignatureValidationFailed(address aggregator);
                          /**
                           * Successful result from simulateValidation.
                           * @param returnInfo gas and time-range returned values
                           * @param senderInfo stake information about the sender
                           * @param factoryInfo stake information about the factory (if any)
                           * @param paymasterInfo stake information about the paymaster (if any)
                           */
                          error ValidationResult(ReturnInfo returnInfo,
                              StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo);
                          /**
                           * Successful result from simulateValidation, if the account returns a signature aggregator
                           * @param returnInfo gas and time-range returned values
                           * @param senderInfo stake information about the sender
                           * @param factoryInfo stake information about the factory (if any)
                           * @param paymasterInfo stake information about the paymaster (if any)
                           * @param aggregatorInfo signature aggregation info (if the account requires signature aggregator)
                           *      bundler MUST use it to verify the signature, or reject the UserOperation
                           */
                          error ValidationResultWithAggregation(ReturnInfo returnInfo,
                              StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo,
                              AggregatorStakeInfo aggregatorInfo);
                          /**
                           * return value of getSenderAddress
                           */
                          error SenderAddressResult(address sender);
                          /**
                           * return value of simulateHandleOp
                           */
                          error ExecutionResult(uint256 preOpGas, uint256 paid, uint48 validAfter, uint48 validUntil, bool targetSuccess, bytes targetResult);
                          //UserOps handled, per aggregator
                          struct UserOpsPerAggregator {
                              UserOperation[] userOps;
                              // aggregator address
                              IAggregator aggregator;
                              // aggregated signature
                              bytes signature;
                          }
                          /**
                           * Execute a batch of UserOperation.
                           * no signature aggregator is used.
                           * if any account requires an aggregator (that is, it returned an aggregator when
                           * performing simulateValidation), then handleAggregatedOps() must be used instead.
                           * @param ops the operations to execute
                           * @param beneficiary the address to receive the fees
                           */
                          function handleOps(UserOperation[] calldata ops, address payable beneficiary) external;
                          /**
                           * Execute a batch of UserOperation with Aggregators
                           * @param opsPerAggregator the operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts)
                           * @param beneficiary the address to receive the fees
                           */
                          function handleAggregatedOps(
                              UserOpsPerAggregator[] calldata opsPerAggregator,
                              address payable beneficiary
                          ) external;
                          /**
                           * generate a request Id - unique identifier for this request.
                           * the request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid.
                           */
                          function getUserOpHash(UserOperation calldata userOp) external view returns (bytes32);
                          /**
                           * Simulate a call to account.validateUserOp and paymaster.validatePaymasterUserOp.
                           * @dev this method always revert. Successful result is ValidationResult error. other errors are failures.
                           * @dev The node must also verify it doesn't use banned opcodes, and that it doesn't reference storage outside the account's data.
                           * @param userOp the user operation to validate.
                           */
                          function simulateValidation(UserOperation calldata userOp) external;
                          /**
                           * gas and return values during simulation
                           * @param preOpGas the gas used for validation (including preValidationGas)
                           * @param prefund the required prefund for this operation
                           * @param sigFailed validateUserOp's (or paymaster's) signature check failed
                           * @param validAfter - first timestamp this UserOp is valid (merging account and paymaster time-range)
                           * @param validUntil - last timestamp this UserOp is valid (merging account and paymaster time-range)
                           * @param paymasterContext returned by validatePaymasterUserOp (to be passed into postOp)
                           */
                          struct ReturnInfo {
                              uint256 preOpGas;
                              uint256 prefund;
                              bool sigFailed;
                              uint48 validAfter;
                              uint48 validUntil;
                              bytes paymasterContext;
                          }
                          /**
                           * returned aggregated signature info.
                           * the aggregator returned by the account, and its current stake.
                           */
                          struct AggregatorStakeInfo {
                              address aggregator;
                              StakeInfo stakeInfo;
                          }
                          /**
                           * Get counterfactual sender address.
                           *  Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation.
                           * this method always revert, and returns the address in SenderAddressResult error
                           * @param initCode the constructor code to be passed into the UserOperation.
                           */
                          function getSenderAddress(bytes memory initCode) external;
                          /**
                           * simulate full execution of a UserOperation (including both validation and target execution)
                           * this method will always revert with "ExecutionResult".
                           * it performs full validation of the UserOperation, but ignores signature error.
                           * an optional target address is called after the userop succeeds, and its value is returned
                           * (before the entire call is reverted)
                           * Note that in order to collect the the success/failure of the target call, it must be executed
                           * with trace enabled to track the emitted events.
                           * @param op the UserOperation to simulate
                           * @param target if nonzero, a target address to call after userop simulation. If called, the targetSuccess and targetResult
                           *        are set to the return from that call.
                           * @param targetCallData callData to pass to target address
                           */
                          function simulateHandleOp(UserOperation calldata op, address target, bytes calldata targetCallData) external;
                      }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      interface INonceManager {
                          /**
                           * Return the next nonce for this sender.
                           * Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop)
                           * But UserOp with different keys can come with arbitrary order.
                           *
                           * @param sender the account address
                           * @param key the high 192 bit of the nonce
                           * @return nonce a full nonce to pass for next UserOp with this sender.
                           */
                          function getNonce(address sender, uint192 key)
                          external view returns (uint256 nonce);
                          /**
                           * Manually increment the nonce of the sender.
                           * This method is exposed just for completeness..
                           * Account does NOT need to call it, neither during validation, nor elsewhere,
                           * as the EntryPoint will update the nonce regardless.
                           * Possible use-case is call it with various keys to "initialize" their nonces to one, so that future
                           * UserOperations will not pay extra for the first transaction with a given key.
                           */
                          function incrementNonce(uint192 key) external;
                      }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      import "./UserOperation.sol";
                      /**
                       * the interface exposed by a paymaster contract, who agrees to pay the gas for user's operations.
                       * a paymaster must hold a stake to cover the required entrypoint stake and also the gas for the transaction.
                       */
                      interface IPaymaster {
                          enum PostOpMode {
                              opSucceeded, // user op succeeded
                              opReverted, // user op reverted. still has to pay for gas.
                              postOpReverted //user op succeeded, but caused postOp to revert. Now it's a 2nd call, after user's op was deliberately reverted.
                          }
                          /**
                           * payment validation: check if paymaster agrees to pay.
                           * Must verify sender is the entryPoint.
                           * Revert to reject this request.
                           * Note that bundlers will reject this method if it changes the state, unless the paymaster is trusted (whitelisted)
                           * The paymaster pre-pays using its deposit, and receive back a refund after the postOp method returns.
                           * @param userOp the user operation
                           * @param userOpHash hash of the user's request data.
                           * @param maxCost the maximum cost of this transaction (based on maximum gas and gas price from userOp)
                           * @return context value to send to a postOp
                           *      zero length to signify postOp is not required.
                           * @return validationData signature and time-range of this operation, encoded the same as the return value of validateUserOperation
                           *      <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
                           *         otherwise, an address of an "authorizer" contract.
                           *      <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite"
                           *      <6-byte> validAfter - first timestamp this operation is valid
                           *      Note that the validation code cannot use block.timestamp (or block.number) directly.
                           */
                          function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost)
                          external returns (bytes memory context, uint256 validationData);
                          /**
                           * post-operation handler.
                           * Must verify sender is the entryPoint
                           * @param mode enum with the following options:
                           *      opSucceeded - user operation succeeded.
                           *      opReverted  - user op reverted. still has to pay for gas.
                           *      postOpReverted - user op succeeded, but caused postOp (in mode=opSucceeded) to revert.
                           *                       Now this is the 2nd call, after user's op was deliberately reverted.
                           * @param context - the context value returned by validatePaymasterUserOp
                           * @param actualGasCost - actual gas used so far (without this postOp call).
                           */
                          function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external;
                      }
                      // SPDX-License-Identifier: GPL-3.0-only
                      pragma solidity ^0.8.12;
                      /**
                       * manage deposits and stakes.
                       * deposit is just a balance used to pay for UserOperations (either by a paymaster or an account)
                       * stake is value locked for at least "unstakeDelay" by the staked entity.
                       */
                      interface IStakeManager {
                          event Deposited(
                              address indexed account,
                              uint256 totalDeposit
                          );
                          event Withdrawn(
                              address indexed account,
                              address withdrawAddress,
                              uint256 amount
                          );
                          /// Emitted when stake or unstake delay are modified
                          event StakeLocked(
                              address indexed account,
                              uint256 totalStaked,
                              uint256 unstakeDelaySec
                          );
                          /// Emitted once a stake is scheduled for withdrawal
                          event StakeUnlocked(
                              address indexed account,
                              uint256 withdrawTime
                          );
                          event StakeWithdrawn(
                              address indexed account,
                              address withdrawAddress,
                              uint256 amount
                          );
                          /**
                           * @param deposit the entity's deposit
                           * @param staked true if this entity is staked.
                           * @param stake actual amount of ether staked for this entity.
                           * @param unstakeDelaySec minimum delay to withdraw the stake.
                           * @param withdrawTime - first block timestamp where 'withdrawStake' will be callable, or zero if already locked
                           * @dev sizes were chosen so that (deposit,staked, stake) fit into one cell (used during handleOps)
                           *    and the rest fit into a 2nd cell.
                           *    112 bit allows for 10^15 eth
                           *    48 bit for full timestamp
                           *    32 bit allows 150 years for unstake delay
                           */
                          struct DepositInfo {
                              uint112 deposit;
                              bool staked;
                              uint112 stake;
                              uint32 unstakeDelaySec;
                              uint48 withdrawTime;
                          }
                          //API struct used by getStakeInfo and simulateValidation
                          struct StakeInfo {
                              uint256 stake;
                              uint256 unstakeDelaySec;
                          }
                          /// @return info - full deposit information of given account
                          function getDepositInfo(address account) external view returns (DepositInfo memory info);
                          /// @return the deposit (for gas payment) of the account
                          function balanceOf(address account) external view returns (uint256);
                          /**
                           * add to the deposit of the given account
                           */
                          function depositTo(address account) external payable;
                          /**
                           * add to the account's stake - amount and delay
                           * any pending unstake is first cancelled.
                           * @param _unstakeDelaySec the new lock duration before the deposit can be withdrawn.
                           */
                          function addStake(uint32 _unstakeDelaySec) external payable;
                          /**
                           * attempt to unlock the stake.
                           * the value can be withdrawn (using withdrawStake) after the unstake delay.
                           */
                          function unlockStake() external;
                          /**
                           * withdraw from the (unlocked) stake.
                           * must first call unlockStake and wait for the unstakeDelay to pass
                           * @param withdrawAddress the address to send withdrawn value.
                           */
                          function withdrawStake(address payable withdrawAddress) external;
                          /**
                           * withdraw from the deposit.
                           * @param withdrawAddress the address to send withdrawn value.
                           * @param withdrawAmount the amount to withdraw.
                           */
                          function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external;
                      }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      /* solhint-disable no-inline-assembly */
                      import {calldataKeccak} from "../core/Helpers.sol";
                      /**
                       * User Operation struct
                       * @param sender the sender account of this request.
                           * @param nonce unique value the sender uses to verify it is not a replay.
                           * @param initCode if set, the account contract will be created by this constructor/
                           * @param callData the method call to execute on this account.
                           * @param callGasLimit the gas limit passed to the callData method call.
                           * @param verificationGasLimit gas used for validateUserOp and validatePaymasterUserOp.
                           * @param preVerificationGas gas not calculated by the handleOps method, but added to the gas paid. Covers batch overhead.
                           * @param maxFeePerGas same as EIP-1559 gas parameter.
                           * @param maxPriorityFeePerGas same as EIP-1559 gas parameter.
                           * @param paymasterAndData if set, this field holds the paymaster address and paymaster-specific data. the paymaster will pay for the transaction instead of the sender.
                           * @param signature sender-verified signature over the entire request, the EntryPoint address and the chain ID.
                           */
                          struct UserOperation {
                              address sender;
                              uint256 nonce;
                              bytes initCode;
                              bytes callData;
                              uint256 callGasLimit;
                              uint256 verificationGasLimit;
                              uint256 preVerificationGas;
                              uint256 maxFeePerGas;
                              uint256 maxPriorityFeePerGas;
                              bytes paymasterAndData;
                              bytes signature;
                          }
                      /**
                       * Utility functions helpful when working with UserOperation structs.
                       */
                      library UserOperationLib {
                          function getSender(UserOperation calldata userOp) internal pure returns (address) {
                              address data;
                              //read sender from userOp, which is first userOp member (saves 800 gas...)
                              assembly {data := calldataload(userOp)}
                              return address(uint160(data));
                          }
                          //relayer/block builder might submit the TX with higher priorityFee, but the user should not
                          // pay above what he signed for.
                          function gasPrice(UserOperation calldata userOp) internal view returns (uint256) {
                          unchecked {
                              uint256 maxFeePerGas = userOp.maxFeePerGas;
                              uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                              if (maxFeePerGas == maxPriorityFeePerGas) {
                                  //legacy mode (for networks that don't support basefee opcode)
                                  return maxFeePerGas;
                              }
                              return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
                          }
                          }
                          function pack(UserOperation calldata userOp) internal pure returns (bytes memory ret) {
                              address sender = getSender(userOp);
                              uint256 nonce = userOp.nonce;
                              bytes32 hashInitCode = calldataKeccak(userOp.initCode);
                              bytes32 hashCallData = calldataKeccak(userOp.callData);
                              uint256 callGasLimit = userOp.callGasLimit;
                              uint256 verificationGasLimit = userOp.verificationGasLimit;
                              uint256 preVerificationGas = userOp.preVerificationGas;
                              uint256 maxFeePerGas = userOp.maxFeePerGas;
                              uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                              bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);
                              return abi.encode(
                                  sender, nonce,
                                  hashInitCode, hashCallData,
                                  callGasLimit, verificationGasLimit, preVerificationGas,
                                  maxFeePerGas, maxPriorityFeePerGas,
                                  hashPaymasterAndData
                              );
                          }
                          function hash(UserOperation calldata userOp) internal pure returns (bytes32) {
                              return keccak256(pack(userOp));
                          }
                          function min(uint256 a, uint256 b) internal pure returns (uint256) {
                              return a < b ? a : b;
                          }
                      }
                      // SPDX-License-Identifier: LGPL-3.0-only
                      pragma solidity >=0.7.5 <0.9.0;
                      // solhint-disable no-inline-assembly
                      /**
                       * Utility functions helpful when making different kinds of contract calls in Solidity.
                       */
                      library Exec {
                          function call(
                              address to,
                              uint256 value,
                              bytes memory data,
                              uint256 txGas
                          ) internal returns (bool success) {
                              assembly {
                                  success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)
                              }
                          }
                          function staticcall(
                              address to,
                              bytes memory data,
                              uint256 txGas
                          ) internal view returns (bool success) {
                              assembly {
                                  success := staticcall(txGas, to, add(data, 0x20), mload(data), 0, 0)
                              }
                          }
                          function delegateCall(
                              address to,
                              bytes memory data,
                              uint256 txGas
                          ) internal returns (bool success) {
                              assembly {
                                  success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0)
                              }
                          }
                          // get returned data from last call or calldelegate
                          function getReturnData(uint256 maxLen) internal pure returns (bytes memory returnData) {
                              assembly {
                                  let len := returndatasize()
                                  if gt(len, maxLen) {
                                      len := maxLen
                                  }
                                  let ptr := mload(0x40)
                                  mstore(0x40, add(ptr, add(len, 0x20)))
                                  mstore(ptr, len)
                                  returndatacopy(add(ptr, 0x20), 0, len)
                                  returnData := ptr
                              }
                          }
                          // revert with explicit byte array (probably reverted info from call)
                          function revertWithData(bytes memory returnData) internal pure {
                              assembly {
                                  revert(add(returnData, 32), mload(returnData))
                              }
                          }
                          function callAndRevert(address to, bytes memory data, uint256 maxLen) internal {
                              bool success = call(to,0,data,gasleft());
                              if (!success) {
                                  revertWithData(getReturnData(maxLen));
                              }
                          }
                      }
                      

                      File 2 of 12: 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 12: Vyper_contract
                      # @version 0.2.8
                      """
                      @title Curve ETH/stETH StableSwap
                      @author Curve.Fi
                      @license Copyright (c) Curve.Fi, 2020 - all rights reserved
                      """
                      
                      from vyper.interfaces import ERC20
                      
                      
                      interface CurveToken:
                          def mint(_to: address, _value: uint256) -> bool: nonpayable
                          def burnFrom(_to: address, _value: uint256) -> bool: nonpayable
                      
                      
                      # Events
                      event TokenExchange:
                          buyer: indexed(address)
                          sold_id: int128
                          tokens_sold: uint256
                          bought_id: int128
                          tokens_bought: uint256
                      
                      event TokenExchangeUnderlying:
                          buyer: indexed(address)
                          sold_id: int128
                          tokens_sold: uint256
                          bought_id: int128
                          tokens_bought: uint256
                      
                      event AddLiquidity:
                          provider: indexed(address)
                          token_amounts: uint256[N_COINS]
                          fees: uint256[N_COINS]
                          invariant: uint256
                          token_supply: uint256
                      
                      event RemoveLiquidity:
                          provider: indexed(address)
                          token_amounts: uint256[N_COINS]
                          fees: uint256[N_COINS]
                          token_supply: uint256
                      
                      event RemoveLiquidityOne:
                          provider: indexed(address)
                          token_amount: uint256
                          coin_amount: uint256
                      
                      event RemoveLiquidityImbalance:
                          provider: indexed(address)
                          token_amounts: uint256[N_COINS]
                          fees: uint256[N_COINS]
                          invariant: uint256
                          token_supply: uint256
                      
                      event CommitNewAdmin:
                          deadline: indexed(uint256)
                          admin: indexed(address)
                      
                      event NewAdmin:
                          admin: indexed(address)
                      
                      event CommitNewFee:
                          deadline: indexed(uint256)
                          fee: uint256
                          admin_fee: uint256
                      
                      event NewFee:
                          fee: uint256
                          admin_fee: uint256
                      
                      event RampA:
                          old_A: uint256
                          new_A: uint256
                          initial_time: uint256
                          future_time: uint256
                      
                      event StopRampA:
                          A: uint256
                          t: uint256
                      
                      
                      # These constants must be set prior to compiling
                      N_COINS: constant(int128) = 2
                      
                      # fixed constants
                      FEE_DENOMINATOR: constant(uint256) = 10 ** 10
                      PRECISION: constant(uint256) = 10 ** 18  # The precision to convert to
                      
                      MAX_ADMIN_FEE: constant(uint256) = 10 * 10 ** 9
                      MAX_FEE: constant(uint256) = 5 * 10 ** 9
                      
                      MAX_A: constant(uint256) = 10 ** 6
                      MAX_A_CHANGE: constant(uint256) = 10
                      A_PRECISION: constant(uint256) = 100
                      
                      ADMIN_ACTIONS_DELAY: constant(uint256) = 3 * 86400
                      MIN_RAMP_TIME: constant(uint256) = 86400
                      
                      coins: public(address[N_COINS])
                      admin_balances: public(uint256[N_COINS])
                      
                      fee: public(uint256)  # fee * 1e10
                      admin_fee: public(uint256)  # admin_fee * 1e10
                      
                      owner: public(address)
                      lp_token: public(address)
                      
                      initial_A: public(uint256)
                      future_A: public(uint256)
                      initial_A_time: public(uint256)
                      future_A_time: public(uint256)
                      
                      admin_actions_deadline: public(uint256)
                      transfer_ownership_deadline: public(uint256)
                      future_fee: public(uint256)
                      future_admin_fee: public(uint256)
                      future_owner: public(address)
                      
                      is_killed: bool
                      kill_deadline: uint256
                      KILL_DEADLINE_DT: constant(uint256) = 2 * 30 * 86400
                      
                      
                      @external
                      def __init__(
                          _owner: address,
                          _coins: address[N_COINS],
                          _pool_token: address,
                          _A: uint256,
                          _fee: uint256,
                          _admin_fee: uint256
                      ):
                          """
                          @notice Contract constructor
                          @param _owner Contract owner address
                          @param _coins Addresses of ERC20 conracts of coins
                          @param _pool_token Address of the token representing LP share
                          @param _A Amplification coefficient multiplied by n * (n - 1)
                          @param _fee Fee to charge for exchanges
                          @param _admin_fee Admin fee
                          """
                          assert _coins[0] == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
                          assert _coins[1] != ZERO_ADDRESS
                      
                          self.coins = _coins
                          self.initial_A = _A * A_PRECISION
                          self.future_A = _A * A_PRECISION
                          self.fee = _fee
                          self.admin_fee = _admin_fee
                          self.owner = _owner
                          self.kill_deadline = block.timestamp + KILL_DEADLINE_DT
                          self.lp_token = _pool_token
                      
                      
                      @view
                      @internal
                      def _A() -> uint256:
                          t1: uint256 = self.future_A_time
                          A1: uint256 = self.future_A
                      
                          if block.timestamp < t1:
                              # handle ramping up and down of A
                              A0: uint256 = self.initial_A
                              t0: uint256 = self.initial_A_time
                              # Expressions in uint256 cannot have negative numbers, thus "if"
                              if A1 > A0:
                                  return A0 + (A1 - A0) * (block.timestamp - t0) / (t1 - t0)
                              else:
                                  return A0 - (A0 - A1) * (block.timestamp - t0) / (t1 - t0)
                      
                          else:  # when t1 == 0 or block.timestamp >= t1
                              return A1
                      
                      
                      @view
                      @external
                      def A() -> uint256:
                          return self._A() / A_PRECISION
                      
                      
                      @view
                      @external
                      def A_precise() -> uint256:
                          return self._A()
                      
                      
                      @view
                      @internal
                      def _balances(_value: uint256 = 0) -> uint256[N_COINS]:
                          return [
                              self.balance - self.admin_balances[0] - _value,
                              ERC20(self.coins[1]).balanceOf(self) - self.admin_balances[1]
                          ]
                      
                      
                      @view
                      @external
                      def balances(i: uint256) -> uint256:
                          """
                          @notice Get the current balance of a coin within the
                                  pool, less the accrued admin fees
                          @param i Index value for the coin to query balance of
                          @return Token balance
                          """
                          return self._balances()[i]
                      
                      
                      @pure
                      @internal
                      def get_D(xp: uint256[N_COINS], amp: uint256) -> uint256:
                          """
                          D invariant calculation in non-overflowing integer operations
                          iteratively
                      
                          A * sum(x_i) * n**n + D = A * D * n**n + D**(n+1) / (n**n * prod(x_i))
                      
                          Converging solution:
                          D[j+1] = (A * n**n * sum(x_i) - D[j]**(n+1) / (n**n prod(x_i))) / (A * n**n - 1)
                          """
                          S: uint256 = 0
                          Dprev: uint256 = 0
                      
                          for _x in xp:
                              S += _x
                          if S == 0:
                              return 0
                      
                          D: uint256 = S
                          Ann: uint256 = amp * N_COINS
                          for _i in range(255):
                              D_P: uint256 = D
                              for _x in xp:
                                  D_P = D_P * D / (_x * N_COINS + 1)  # +1 is to prevent /0
                              Dprev = D
                              D = (Ann * S / A_PRECISION + D_P * N_COINS) * D / ((Ann - A_PRECISION) * D / A_PRECISION + (N_COINS + 1) * D_P)
                              # Equality with the precision of 1
                              if D > Dprev:
                                  if D - Dprev <= 1:
                                      return D
                              else:
                                  if Dprev - D <= 1:
                                      return D
                          # convergence typically occurs in 4 rounds or less, this should be unreachable!
                          # if it does happen the pool is borked and LPs can withdraw via `remove_liquidity`
                          raise
                      
                      
                      @view
                      @external
                      def get_virtual_price() -> uint256:
                          """
                          @notice The current virtual price of the pool LP token
                          @dev Useful for calculating profits
                          @return LP token virtual price normalized to 1e18
                          """
                          D: uint256 = self.get_D(self._balances(), self._A())
                          # D is in the units similar to DAI (e.g. converted to precision 1e18)
                          # When balanced, D = n * x_u - total virtual value of the portfolio
                          token_supply: uint256 = ERC20(self.lp_token).totalSupply()
                          return D * PRECISION / token_supply
                      
                      
                      @view
                      @external
                      def calc_token_amount(amounts: uint256[N_COINS], is_deposit: bool) -> uint256:
                          """
                          @notice Calculate addition or reduction in token supply from a deposit or withdrawal
                          @dev This calculation accounts for slippage, but not fees.
                               Needed to prevent front-running, not for precise calculations!
                          @param amounts Amount of each coin being deposited
                          @param is_deposit set True for deposits, False for withdrawals
                          @return Expected amount of LP tokens received
                          """
                          amp: uint256 = self._A()
                          balances: uint256[N_COINS] = self._balances()
                          D0: uint256 = self.get_D(balances, amp)
                          for i in range(N_COINS):
                              if is_deposit:
                                  balances[i] += amounts[i]
                              else:
                                  balances[i] -= amounts[i]
                          D1: uint256 = self.get_D(balances, amp)
                          token_amount: uint256 = ERC20(self.lp_token).totalSupply()
                          diff: uint256 = 0
                          if is_deposit:
                              diff = D1 - D0
                          else:
                              diff = D0 - D1
                          return diff * token_amount / D0
                      
                      
                      @payable
                      @external
                      @nonreentrant('lock')
                      def add_liquidity(amounts: uint256[N_COINS], min_mint_amount: uint256) -> uint256:
                          """
                          @notice Deposit coins into the pool
                          @param amounts List of amounts of coins to deposit
                          @param min_mint_amount Minimum amount of LP tokens to mint from the deposit
                          @return Amount of LP tokens received by depositing
                          """
                          assert not self.is_killed  # dev: is killed
                      
                          # Initial invariant
                          amp: uint256 = self._A()
                          old_balances: uint256[N_COINS] = self._balances(msg.value)
                          D0: uint256 = self.get_D(old_balances, amp)
                      
                          lp_token: address = self.lp_token
                          token_supply: uint256 = ERC20(lp_token).totalSupply()
                          new_balances: uint256[N_COINS] = old_balances
                          for i in range(N_COINS):
                              if token_supply == 0:
                                  assert amounts[i] > 0  # dev: initial deposit requires all coins
                              new_balances[i] += amounts[i]
                      
                          # Invariant after change
                          D1: uint256 = self.get_D(new_balances, amp)
                          assert D1 > D0
                      
                          # We need to recalculate the invariant accounting for fees
                          # to calculate fair user's share
                          fees: uint256[N_COINS] = empty(uint256[N_COINS])
                          mint_amount: uint256 = 0
                          D2: uint256 = 0
                          if token_supply > 0:
                              # Only account for fees if we are not the first to deposit
                              fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
                              admin_fee: uint256 = self.admin_fee
                              for i in range(N_COINS):
                                  ideal_balance: uint256 = D1 * old_balances[i] / D0
                                  difference: uint256 = 0
                                  if ideal_balance > new_balances[i]:
                                      difference = ideal_balance - new_balances[i]
                                  else:
                                      difference = new_balances[i] - ideal_balance
                                  fees[i] = fee * difference / FEE_DENOMINATOR
                                  if admin_fee != 0:
                                      self.admin_balances[i] += fees[i] * admin_fee / FEE_DENOMINATOR
                                  new_balances[i] -= fees[i]
                              D2 = self.get_D(new_balances, amp)
                              mint_amount = token_supply * (D2 - D0) / D0
                          else:
                              mint_amount = D1  # Take the dust if there was any
                      
                          assert mint_amount >= min_mint_amount, "Slippage screwed you"
                      
                          # Take coins from the sender
                          assert msg.value == amounts[0]
                          if amounts[1] > 0:
                              assert ERC20(self.coins[1]).transferFrom(msg.sender, self, amounts[1])
                      
                          # Mint pool tokens
                          CurveToken(lp_token).mint(msg.sender, mint_amount)
                      
                          log AddLiquidity(msg.sender, amounts, fees, D1, token_supply + mint_amount)
                      
                          return mint_amount
                      
                      
                      @view
                      @internal
                      def get_y(i: int128, j: int128, x: uint256, xp: uint256[N_COINS]) -> uint256:
                          """
                          Calculate x[j] if one makes x[i] = x
                      
                          Done by solving quadratic equation iteratively.
                          x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
                          x_1**2 + b*x_1 = c
                      
                          x_1 = (x_1**2 + c) / (2*x_1 + b)
                          """
                          # x in the input is converted to the same price/precision
                      
                          assert i != j       # dev: same coin
                          assert j >= 0       # dev: j below zero
                          assert j < N_COINS  # dev: j above N_COINS
                      
                          # should be unreachable, but good for safety
                          assert i >= 0
                          assert i < N_COINS
                      
                          amp: uint256 = self._A()
                          D: uint256 = self.get_D(xp, amp)
                          Ann: uint256 = amp * N_COINS
                          c: uint256 = D
                          S_: uint256 = 0
                          _x: uint256 = 0
                          y_prev: uint256 = 0
                      
                          for _i in range(N_COINS):
                              if _i == i:
                                  _x = x
                              elif _i != j:
                                  _x = xp[_i]
                              else:
                                  continue
                              S_ += _x
                              c = c * D / (_x * N_COINS)
                          c = c * D * A_PRECISION / (Ann * N_COINS)
                          b: uint256 = S_ + D * A_PRECISION / Ann  # - D
                          y: uint256 = D
                          for _i in range(255):
                              y_prev = y
                              y = (y*y + c) / (2 * y + b - D)
                              # Equality with the precision of 1
                              if y > y_prev:
                                  if y - y_prev <= 1:
                                      return y
                              else:
                                  if y_prev - y <= 1:
                                      return y
                          raise
                      
                      
                      @view
                      @external
                      def get_dy(i: int128, j: int128, dx: uint256) -> uint256:
                          xp: uint256[N_COINS] = self._balances()
                          x: uint256 = xp[i] + dx
                          y: uint256 = self.get_y(i, j, x, xp)
                          dy: uint256 = xp[j] - y - 1
                          fee: uint256 = self.fee * dy / FEE_DENOMINATOR
                          return dy - fee
                      
                      
                      @payable
                      @external
                      @nonreentrant('lock')
                      def exchange(i: int128, j: int128, dx: uint256, min_dy: uint256) -> uint256:
                          """
                          @notice Perform an exchange between two coins
                          @dev Index values can be found via the `coins` public getter method
                          @param i Index value for the coin to send
                          @param j Index valie of the coin to recieve
                          @param dx Amount of `i` being exchanged
                          @param min_dy Minimum amount of `j` to receive
                          @return Actual amount of `j` received
                          """
                          assert not self.is_killed  # dev: is killed
                          # dx and dy are in aTokens
                      
                          xp: uint256[N_COINS] = self._balances(msg.value)
                      
                          x: uint256 = xp[i] + dx
                          y: uint256 = self.get_y(i, j, x, xp)
                          dy: uint256 = xp[j] - y - 1
                          dy_fee: uint256 = dy * self.fee / FEE_DENOMINATOR
                      
                          # Convert all to real units
                          dy = dy - dy_fee
                          assert dy >= min_dy, "Exchange resulted in fewer coins than expected"
                      
                          admin_fee: uint256 = self.admin_fee
                          if admin_fee != 0:
                              dy_admin_fee: uint256 = dy_fee * admin_fee / FEE_DENOMINATOR
                              if dy_admin_fee != 0:
                                  self.admin_balances[j] += dy_admin_fee
                      
                          coin: address = self.coins[1]
                          if i == 0:
                              assert msg.value == dx
                              assert ERC20(coin).transfer(msg.sender, dy)
                          else:
                              assert msg.value == 0
                              assert ERC20(coin).transferFrom(msg.sender, self, dx)
                              raw_call(msg.sender, b"", value=dy)
                      
                          log TokenExchange(msg.sender, i, dx, j, dy)
                      
                          return dy
                      
                      
                      @external
                      @nonreentrant('lock')
                      def remove_liquidity(
                          _amount: uint256,
                          _min_amounts: uint256[N_COINS],
                      ) -> uint256[N_COINS]:
                          """
                          @notice Withdraw coins from the pool
                          @dev Withdrawal amounts are based on current deposit ratios
                          @param _amount Quantity of LP tokens to burn in the withdrawal
                          @param _min_amounts Minimum amounts of underlying coins to receive
                          @return List of amounts of coins that were withdrawn
                          """
                          amounts: uint256[N_COINS] = self._balances()
                          lp_token: address = self.lp_token
                          total_supply: uint256 = ERC20(lp_token).totalSupply()
                          CurveToken(lp_token).burnFrom(msg.sender, _amount)  # dev: insufficient funds
                      
                          for i in range(N_COINS):
                              value: uint256 = amounts[i] * _amount / total_supply
                              assert value >= _min_amounts[i], "Withdrawal resulted in fewer coins than expected"
                      
                              amounts[i] = value
                              if i == 0:
                                  raw_call(msg.sender, b"", value=value)
                              else:
                                  assert ERC20(self.coins[1]).transfer(msg.sender, value)
                      
                          log RemoveLiquidity(msg.sender, amounts, empty(uint256[N_COINS]), total_supply - _amount)
                      
                          return amounts
                      
                      
                      @external
                      @nonreentrant('lock')
                      def remove_liquidity_imbalance(
                          _amounts: uint256[N_COINS],
                          _max_burn_amount: uint256
                      ) -> uint256:
                          """
                          @notice Withdraw coins from the pool in an imbalanced amount
                          @param _amounts List of amounts of underlying coins to withdraw
                          @param _max_burn_amount Maximum amount of LP token to burn in the withdrawal
                          @return Actual amount of the LP token burned in the withdrawal
                          """
                          assert not self.is_killed  # dev: is killed
                      
                          amp: uint256 = self._A()
                          old_balances: uint256[N_COINS] = self._balances()
                          D0: uint256 = self.get_D(old_balances, amp)
                          new_balances: uint256[N_COINS] = old_balances
                          for i in range(N_COINS):
                              new_balances[i] -= _amounts[i]
                          D1: uint256 = self.get_D(new_balances, amp)
                      
                          fees: uint256[N_COINS] = empty(uint256[N_COINS])
                          fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
                          admin_fee: uint256 = self.admin_fee
                          for i in range(N_COINS):
                              ideal_balance: uint256 = D1 * old_balances[i] / D0
                              new_balance: uint256 = new_balances[i]
                              difference: uint256 = 0
                              if ideal_balance > new_balance:
                                  difference = ideal_balance - new_balance
                              else:
                                  difference = new_balance - ideal_balance
                              fees[i] = fee * difference / FEE_DENOMINATOR
                              if admin_fee != 0:
                                  self.admin_balances[i] += fees[i] * admin_fee / FEE_DENOMINATOR
                              new_balances[i] -= fees[i]
                          D2: uint256 = self.get_D(new_balances, amp)
                      
                          lp_token: address = self.lp_token
                          token_supply: uint256 = ERC20(lp_token).totalSupply()
                          token_amount: uint256 = (D0 - D2) * token_supply / D0
                      
                          assert token_amount != 0  # dev: zero tokens burned
                          assert token_amount <= _max_burn_amount, "Slippage screwed you"
                      
                          CurveToken(lp_token).burnFrom(msg.sender, token_amount)  # dev: insufficient funds
                      
                          if _amounts[0] != 0:
                              raw_call(msg.sender, b"", value=_amounts[0])
                          if _amounts[1] != 0:
                              assert ERC20(self.coins[1]).transfer(msg.sender, _amounts[1])
                      
                          log RemoveLiquidityImbalance(msg.sender, _amounts, fees, D1, token_supply - token_amount)
                      
                          return token_amount
                      
                      
                      @pure
                      @internal
                      def get_y_D(A_: uint256, i: int128, xp: uint256[N_COINS], D: uint256) -> uint256:
                          """
                          Calculate x[i] if one reduces D from being calculated for xp to D
                      
                          Done by solving quadratic equation iteratively.
                          x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
                          x_1**2 + b*x_1 = c
                      
                          x_1 = (x_1**2 + c) / (2*x_1 + b)
                          """
                          # x in the input is converted to the same price/precision
                      
                          assert i >= 0       # dev: i below zero
                          assert i < N_COINS  # dev: i above N_COINS
                      
                          Ann: uint256 = A_ * N_COINS
                          c: uint256 = D
                          S_: uint256 = 0
                          _x: uint256 = 0
                          y_prev: uint256 = 0
                      
                          for _i in range(N_COINS):
                              if _i != i:
                                  _x = xp[_i]
                              else:
                                  continue
                              S_ += _x
                              c = c * D / (_x * N_COINS)
                          c = c * D * A_PRECISION / (Ann * N_COINS)
                          b: uint256 = S_ + D * A_PRECISION / Ann
                          y: uint256 = D
                      
                          for _i in range(255):
                              y_prev = y
                              y = (y*y + c) / (2 * y + b - D)
                              # Equality with the precision of 1
                              if y > y_prev:
                                  if y - y_prev <= 1:
                                      return y
                              else:
                                  if y_prev - y <= 1:
                                      return y
                          raise
                      
                      
                      @view
                      @internal
                      def _calc_withdraw_one_coin(_token_amount: uint256, i: int128) -> (uint256, uint256):
                          # First, need to calculate
                          # * Get current D
                          # * Solve Eqn against y_i for D - _token_amount
                          amp: uint256 = self._A()
                          xp: uint256[N_COINS] = self._balances()
                          D0: uint256 = self.get_D(xp, amp)
                          total_supply: uint256 = ERC20(self.lp_token).totalSupply()
                          D1: uint256 = D0 - _token_amount * D0 / total_supply
                          new_y: uint256 = self.get_y_D(amp, i, xp, D1)
                      
                          fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
                          xp_reduced: uint256[N_COINS] = xp
                          for j in range(N_COINS):
                              dx_expected: uint256 = 0
                              if j == i:
                                  dx_expected = xp[j] * D1 / D0 - new_y
                              else:
                                  dx_expected = xp[j] - xp[j] * D1 / D0
                              xp_reduced[j] -= fee * dx_expected / FEE_DENOMINATOR
                      
                          dy: uint256 = xp_reduced[i] - self.get_y_D(amp, i, xp_reduced, D1)
                      
                          dy -= 1  # Withdraw less to account for rounding errors
                          dy_0: uint256 = xp[i] - new_y  # w/o fees
                      
                          return dy, dy_0 - dy
                      
                      
                      @view
                      @external
                      def calc_withdraw_one_coin(_token_amount: uint256, i: int128) -> uint256:
                          """
                          @notice Calculate the amount received when withdrawing a single coin
                          @dev Result is the same for underlying or wrapped asset withdrawals
                          @param _token_amount Amount of LP tokens to burn in the withdrawal
                          @param i Index value of the coin to withdraw
                          @return Amount of coin received
                          """
                          return self._calc_withdraw_one_coin(_token_amount, i)[0]
                      
                      
                      @external
                      @nonreentrant('lock')
                      def remove_liquidity_one_coin(
                          _token_amount: uint256,
                          i: int128,
                          _min_amount: uint256
                      ) -> uint256:
                          """
                          @notice Withdraw a single coin from the pool
                          @param _token_amount Amount of LP tokens to burn in the withdrawal
                          @param i Index value of the coin to withdraw
                          @param _min_amount Minimum amount of coin to receive
                          @return Amount of coin received
                          """
                          assert not self.is_killed  # dev: is killed
                      
                          dy: uint256 = 0
                          dy_fee: uint256 = 0
                          dy, dy_fee = self._calc_withdraw_one_coin(_token_amount, i)
                      
                          assert dy >= _min_amount, "Not enough coins removed"
                      
                          self.admin_balances[i] += dy_fee * self.admin_fee / FEE_DENOMINATOR
                      
                          CurveToken(self.lp_token).burnFrom(msg.sender, _token_amount)  # dev: insufficient funds
                      
                          if i == 0:
                              raw_call(msg.sender, b"", value=dy)
                          else:
                              assert ERC20(self.coins[1]).transfer(msg.sender, dy)
                      
                          log RemoveLiquidityOne(msg.sender, _token_amount, dy)
                      
                          return dy
                      
                      
                      ### Admin functions ###
                      
                      @external
                      def ramp_A(_future_A: uint256, _future_time: uint256):
                          assert msg.sender == self.owner  # dev: only owner
                          assert block.timestamp >= self.initial_A_time + MIN_RAMP_TIME
                          assert _future_time >= block.timestamp + MIN_RAMP_TIME  # dev: insufficient time
                      
                          _initial_A: uint256 = self._A()
                          _future_A_p: uint256 = _future_A * A_PRECISION
                      
                          assert _future_A > 0 and _future_A < MAX_A
                          if _future_A_p < _initial_A:
                              assert _future_A_p * MAX_A_CHANGE >= _initial_A
                          else:
                              assert _future_A_p <= _initial_A * MAX_A_CHANGE
                      
                          self.initial_A = _initial_A
                          self.future_A = _future_A_p
                          self.initial_A_time = block.timestamp
                          self.future_A_time = _future_time
                      
                          log RampA(_initial_A, _future_A_p, block.timestamp, _future_time)
                      
                      
                      @external
                      def stop_ramp_A():
                          assert msg.sender == self.owner  # dev: only owner
                      
                          current_A: uint256 = self._A()
                          self.initial_A = current_A
                          self.future_A = current_A
                          self.initial_A_time = block.timestamp
                          self.future_A_time = block.timestamp
                          # now (block.timestamp < t1) is always False, so we return saved A
                      
                          log StopRampA(current_A, block.timestamp)
                      
                      
                      @external
                      def commit_new_fee(new_fee: uint256, new_admin_fee: uint256):
                          assert msg.sender == self.owner  # dev: only owner
                          assert self.admin_actions_deadline == 0  # dev: active action
                          assert new_fee <= MAX_FEE  # dev: fee exceeds maximum
                          assert new_admin_fee <= MAX_ADMIN_FEE  # dev: admin fee exceeds maximum
                      
                          _deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY
                          self.admin_actions_deadline = _deadline
                          self.future_fee = new_fee
                          self.future_admin_fee = new_admin_fee
                      
                          log CommitNewFee(_deadline, new_fee, new_admin_fee)
                      
                      
                      @external
                      @nonreentrant('lock')
                      def apply_new_fee():
                          assert msg.sender == self.owner  # dev: only owner
                          assert block.timestamp >= self.admin_actions_deadline  # dev: insufficient time
                          assert self.admin_actions_deadline != 0  # dev: no active action
                      
                          self.admin_actions_deadline = 0
                          _fee: uint256 = self.future_fee
                          _admin_fee: uint256 = self.future_admin_fee
                          self.fee = _fee
                          self.admin_fee = _admin_fee
                      
                          log NewFee(_fee, _admin_fee)
                      
                      
                      @external
                      def revert_new_parameters():
                          assert msg.sender == self.owner  # dev: only owner
                      
                          self.admin_actions_deadline = 0
                      
                      
                      @external
                      def commit_transfer_ownership(_owner: address):
                          assert msg.sender == self.owner  # dev: only owner
                          assert self.transfer_ownership_deadline == 0  # dev: active transfer
                      
                          _deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY
                          self.transfer_ownership_deadline = _deadline
                          self.future_owner = _owner
                      
                          log CommitNewAdmin(_deadline, _owner)
                      
                      
                      @external
                      @nonreentrant('lock')
                      def apply_transfer_ownership():
                          assert msg.sender == self.owner  # dev: only owner
                          assert block.timestamp >= self.transfer_ownership_deadline  # dev: insufficient time
                          assert self.transfer_ownership_deadline != 0  # dev: no active transfer
                      
                          self.transfer_ownership_deadline = 0
                          _owner: address = self.future_owner
                          self.owner = _owner
                      
                          log NewAdmin(_owner)
                      
                      
                      @external
                      def revert_transfer_ownership():
                          assert msg.sender == self.owner  # dev: only owner
                      
                          self.transfer_ownership_deadline = 0
                      
                      
                      @external
                      @nonreentrant('lock')
                      def withdraw_admin_fees():
                          assert msg.sender == self.owner  # dev: only owner
                      
                          amount: uint256 = self.admin_balances[0]
                          if amount != 0:
                              raw_call(msg.sender, b"", value=amount)
                      
                          amount = self.admin_balances[1]
                          if amount != 0:
                              assert ERC20(self.coins[1]).transfer(msg.sender, amount)
                      
                          self.admin_balances = empty(uint256[N_COINS])
                      
                      
                      @external
                      def donate_admin_fees():
                          """
                          Just in case admin balances somehow become higher than total (rounding error?)
                          this can be used to fix the state, too
                          """
                          assert msg.sender == self.owner  # dev: only owner
                          self.admin_balances = empty(uint256[N_COINS])
                      
                      
                      @external
                      def kill_me():
                          assert msg.sender == self.owner  # dev: only owner
                          assert self.kill_deadline > block.timestamp  # dev: deadline has passed
                          self.is_killed = True
                      
                      
                      @external
                      def unkill_me():
                          assert msg.sender == self.owner  # dev: only owner
                          self.is_killed = False

                      File 4 of 12: WETH9
                      // Copyright (C) 2015, 2016, 2017 Dapphub
                      
                      // This program is free software: you can redistribute it and/or modify
                      // it under the terms of the GNU General Public License as published by
                      // the Free Software Foundation, either version 3 of the License, or
                      // (at your option) any later version.
                      
                      // This program is distributed in the hope that it will be useful,
                      // but WITHOUT ANY WARRANTY; without even the implied warranty of
                      // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                      // GNU General Public License for more details.
                      
                      // You should have received a copy of the GNU General Public License
                      // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                      
                      pragma solidity ^0.4.18;
                      
                      contract WETH9 {
                          string public name     = "Wrapped Ether";
                          string public symbol   = "WETH";
                          uint8  public decimals = 18;
                      
                          event  Approval(address indexed src, address indexed guy, uint wad);
                          event  Transfer(address indexed src, address indexed dst, uint wad);
                          event  Deposit(address indexed dst, uint wad);
                          event  Withdrawal(address indexed src, uint wad);
                      
                          mapping (address => uint)                       public  balanceOf;
                          mapping (address => mapping (address => uint))  public  allowance;
                      
                          function() public payable {
                              deposit();
                          }
                          function deposit() public payable {
                              balanceOf[msg.sender] += msg.value;
                              Deposit(msg.sender, msg.value);
                          }
                          function withdraw(uint wad) public {
                              require(balanceOf[msg.sender] >= wad);
                              balanceOf[msg.sender] -= wad;
                              msg.sender.transfer(wad);
                              Withdrawal(msg.sender, wad);
                          }
                      
                          function totalSupply() public view returns (uint) {
                              return this.balance;
                          }
                      
                          function approve(address guy, uint wad) public returns (bool) {
                              allowance[msg.sender][guy] = wad;
                              Approval(msg.sender, guy, wad);
                              return true;
                          }
                      
                          function transfer(address dst, uint wad) public returns (bool) {
                              return transferFrom(msg.sender, dst, wad);
                          }
                      
                          function transferFrom(address src, address dst, uint wad)
                              public
                              returns (bool)
                          {
                              require(balanceOf[src] >= wad);
                      
                              if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
                                  require(allowance[src][msg.sender] >= wad);
                                  allowance[src][msg.sender] -= wad;
                              }
                      
                              balanceOf[src] -= wad;
                              balanceOf[dst] += wad;
                      
                              Transfer(src, dst, wad);
                      
                              return true;
                          }
                      }
                      
                      
                      /*
                                          GNU GENERAL PUBLIC LICENSE
                                             Version 3, 29 June 2007
                      
                       Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
                       Everyone is permitted to copy and distribute verbatim copies
                       of this license document, but changing it is not allowed.
                      
                                                  Preamble
                      
                        The GNU General Public License is a free, copyleft license for
                      software and other kinds of works.
                      
                        The licenses for most software and other practical works are designed
                      to take away your freedom to share and change the works.  By contrast,
                      the GNU General Public License is intended to guarantee your freedom to
                      share and change all versions of a program--to make sure it remains free
                      software for all its users.  We, the Free Software Foundation, use the
                      GNU General Public License for most of our software; it applies also to
                      any other work released this way by its authors.  You can apply it to
                      your programs, too.
                      
                        When we speak of free software, we are referring to freedom, not
                      price.  Our General Public Licenses are designed to make sure that you
                      have the freedom to distribute copies of free software (and charge for
                      them if you wish), that you receive source code or can get it if you
                      want it, that you can change the software or use pieces of it in new
                      free programs, and that you know you can do these things.
                      
                        To protect your rights, we need to prevent others from denying you
                      these rights or asking you to surrender the rights.  Therefore, you have
                      certain responsibilities if you distribute copies of the software, or if
                      you modify it: responsibilities to respect the freedom of others.
                      
                        For example, if you distribute copies of such a program, whether
                      gratis or for a fee, you must pass on to the recipients the same
                      freedoms that you received.  You must make sure that they, too, receive
                      or can get the source code.  And you must show them these terms so they
                      know their rights.
                      
                        Developers that use the GNU GPL protect your rights with two steps:
                      (1) assert copyright on the software, and (2) offer you this License
                      giving you legal permission to copy, distribute and/or modify it.
                      
                        For the developers' and authors' protection, the GPL clearly explains
                      that there is no warranty for this free software.  For both users' and
                      authors' sake, the GPL requires that modified versions be marked as
                      changed, so that their problems will not be attributed erroneously to
                      authors of previous versions.
                      
                        Some devices are designed to deny users access to install or run
                      modified versions of the software inside them, although the manufacturer
                      can do so.  This is fundamentally incompatible with the aim of
                      protecting users' freedom to change the software.  The systematic
                      pattern of such abuse occurs in the area of products for individuals to
                      use, which is precisely where it is most unacceptable.  Therefore, we
                      have designed this version of the GPL to prohibit the practice for those
                      products.  If such problems arise substantially in other domains, we
                      stand ready to extend this provision to those domains in future versions
                      of the GPL, as needed to protect the freedom of users.
                      
                        Finally, every program is threatened constantly by software patents.
                      States should not allow patents to restrict development and use of
                      software on general-purpose computers, but in those that do, we wish to
                      avoid the special danger that patents applied to a free program could
                      make it effectively proprietary.  To prevent this, the GPL assures that
                      patents cannot be used to render the program non-free.
                      
                        The precise terms and conditions for copying, distribution and
                      modification follow.
                      
                                             TERMS AND CONDITIONS
                      
                        0. Definitions.
                      
                        "This License" refers to version 3 of the GNU General Public License.
                      
                        "Copyright" also means copyright-like laws that apply to other kinds of
                      works, such as semiconductor masks.
                      
                        "The Program" refers to any copyrightable work licensed under this
                      License.  Each licensee is addressed as "you".  "Licensees" and
                      "recipients" may be individuals or organizations.
                      
                        To "modify" a work means to copy from or adapt all or part of the work
                      in a fashion requiring copyright permission, other than the making of an
                      exact copy.  The resulting work is called a "modified version" of the
                      earlier work or a work "based on" the earlier work.
                      
                        A "covered work" means either the unmodified Program or a work based
                      on the Program.
                      
                        To "propagate" a work means to do anything with it that, without
                      permission, would make you directly or secondarily liable for
                      infringement under applicable copyright law, except executing it on a
                      computer or modifying a private copy.  Propagation includes copying,
                      distribution (with or without modification), making available to the
                      public, and in some countries other activities as well.
                      
                        To "convey" a work means any kind of propagation that enables other
                      parties to make or receive copies.  Mere interaction with a user through
                      a computer network, with no transfer of a copy, is not conveying.
                      
                        An interactive user interface displays "Appropriate Legal Notices"
                      to the extent that it includes a convenient and prominently visible
                      feature that (1) displays an appropriate copyright notice, and (2)
                      tells the user that there is no warranty for the work (except to the
                      extent that warranties are provided), that licensees may convey the
                      work under this License, and how to view a copy of this License.  If
                      the interface presents a list of user commands or options, such as a
                      menu, a prominent item in the list meets this criterion.
                      
                        1. Source Code.
                      
                        The "source code" for a work means the preferred form of the work
                      for making modifications to it.  "Object code" means any non-source
                      form of a work.
                      
                        A "Standard Interface" means an interface that either is an official
                      standard defined by a recognized standards body, or, in the case of
                      interfaces specified for a particular programming language, one that
                      is widely used among developers working in that language.
                      
                        The "System Libraries" of an executable work include anything, other
                      than the work as a whole, that (a) is included in the normal form of
                      packaging a Major Component, but which is not part of that Major
                      Component, and (b) serves only to enable use of the work with that
                      Major Component, or to implement a Standard Interface for which an
                      implementation is available to the public in source code form.  A
                      "Major Component", in this context, means a major essential component
                      (kernel, window system, and so on) of the specific operating system
                      (if any) on which the executable work runs, or a compiler used to
                      produce the work, or an object code interpreter used to run it.
                      
                        The "Corresponding Source" for a work in object code form means all
                      the source code needed to generate, install, and (for an executable
                      work) run the object code and to modify the work, including scripts to
                      control those activities.  However, it does not include the work's
                      System Libraries, or general-purpose tools or generally available free
                      programs which are used unmodified in performing those activities but
                      which are not part of the work.  For example, Corresponding Source
                      includes interface definition files associated with source files for
                      the work, and the source code for shared libraries and dynamically
                      linked subprograms that the work is specifically designed to require,
                      such as by intimate data communication or control flow between those
                      subprograms and other parts of the work.
                      
                        The Corresponding Source need not include anything that users
                      can regenerate automatically from other parts of the Corresponding
                      Source.
                      
                        The Corresponding Source for a work in source code form is that
                      same work.
                      
                        2. Basic Permissions.
                      
                        All rights granted under this License are granted for the term of
                      copyright on the Program, and are irrevocable provided the stated
                      conditions are met.  This License explicitly affirms your unlimited
                      permission to run the unmodified Program.  The output from running a
                      covered work is covered by this License only if the output, given its
                      content, constitutes a covered work.  This License acknowledges your
                      rights of fair use or other equivalent, as provided by copyright law.
                      
                        You may make, run and propagate covered works that you do not
                      convey, without conditions so long as your license otherwise remains
                      in force.  You may convey covered works to others for the sole purpose
                      of having them make modifications exclusively for you, or provide you
                      with facilities for running those works, provided that you comply with
                      the terms of this License in conveying all material for which you do
                      not control copyright.  Those thus making or running the covered works
                      for you must do so exclusively on your behalf, under your direction
                      and control, on terms that prohibit them from making any copies of
                      your copyrighted material outside their relationship with you.
                      
                        Conveying under any other circumstances is permitted solely under
                      the conditions stated below.  Sublicensing is not allowed; section 10
                      makes it unnecessary.
                      
                        3. Protecting Users' Legal Rights From Anti-Circumvention Law.
                      
                        No covered work shall be deemed part of an effective technological
                      measure under any applicable law fulfilling obligations under article
                      11 of the WIPO copyright treaty adopted on 20 December 1996, or
                      similar laws prohibiting or restricting circumvention of such
                      measures.
                      
                        When you convey a covered work, you waive any legal power to forbid
                      circumvention of technological measures to the extent such circumvention
                      is effected by exercising rights under this License with respect to
                      the covered work, and you disclaim any intention to limit operation or
                      modification of the work as a means of enforcing, against the work's
                      users, your or third parties' legal rights to forbid circumvention of
                      technological measures.
                      
                        4. Conveying Verbatim Copies.
                      
                        You may convey verbatim copies of the Program's source code as you
                      receive it, in any medium, provided that you conspicuously and
                      appropriately publish on each copy an appropriate copyright notice;
                      keep intact all notices stating that this License and any
                      non-permissive terms added in accord with section 7 apply to the code;
                      keep intact all notices of the absence of any warranty; and give all
                      recipients a copy of this License along with the Program.
                      
                        You may charge any price or no price for each copy that you convey,
                      and you may offer support or warranty protection for a fee.
                      
                        5. Conveying Modified Source Versions.
                      
                        You may convey a work based on the Program, or the modifications to
                      produce it from the Program, in the form of source code under the
                      terms of section 4, provided that you also meet all of these conditions:
                      
                          a) The work must carry prominent notices stating that you modified
                          it, and giving a relevant date.
                      
                          b) The work must carry prominent notices stating that it is
                          released under this License and any conditions added under section
                          7.  This requirement modifies the requirement in section 4 to
                          "keep intact all notices".
                      
                          c) You must license the entire work, as a whole, under this
                          License to anyone who comes into possession of a copy.  This
                          License will therefore apply, along with any applicable section 7
                          additional terms, to the whole of the work, and all its parts,
                          regardless of how they are packaged.  This License gives no
                          permission to license the work in any other way, but it does not
                          invalidate such permission if you have separately received it.
                      
                          d) If the work has interactive user interfaces, each must display
                          Appropriate Legal Notices; however, if the Program has interactive
                          interfaces that do not display Appropriate Legal Notices, your
                          work need not make them do so.
                      
                        A compilation of a covered work with other separate and independent
                      works, which are not by their nature extensions of the covered work,
                      and which are not combined with it such as to form a larger program,
                      in or on a volume of a storage or distribution medium, is called an
                      "aggregate" if the compilation and its resulting copyright are not
                      used to limit the access or legal rights of the compilation's users
                      beyond what the individual works permit.  Inclusion of a covered work
                      in an aggregate does not cause this License to apply to the other
                      parts of the aggregate.
                      
                        6. Conveying Non-Source Forms.
                      
                        You may convey a covered work in object code form under the terms
                      of sections 4 and 5, provided that you also convey the
                      machine-readable Corresponding Source under the terms of this License,
                      in one of these ways:
                      
                          a) Convey the object code in, or embodied in, a physical product
                          (including a physical distribution medium), accompanied by the
                          Corresponding Source fixed on a durable physical medium
                          customarily used for software interchange.
                      
                          b) Convey the object code in, or embodied in, a physical product
                          (including a physical distribution medium), accompanied by a
                          written offer, valid for at least three years and valid for as
                          long as you offer spare parts or customer support for that product
                          model, to give anyone who possesses the object code either (1) a
                          copy of the Corresponding Source for all the software in the
                          product that is covered by this License, on a durable physical
                          medium customarily used for software interchange, for a price no
                          more than your reasonable cost of physically performing this
                          conveying of source, or (2) access to copy the
                          Corresponding Source from a network server at no charge.
                      
                          c) Convey individual copies of the object code with a copy of the
                          written offer to provide the Corresponding Source.  This
                          alternative is allowed only occasionally and noncommercially, and
                          only if you received the object code with such an offer, in accord
                          with subsection 6b.
                      
                          d) Convey the object code by offering access from a designated
                          place (gratis or for a charge), and offer equivalent access to the
                          Corresponding Source in the same way through the same place at no
                          further charge.  You need not require recipients to copy the
                          Corresponding Source along with the object code.  If the place to
                          copy the object code is a network server, the Corresponding Source
                          may be on a different server (operated by you or a third party)
                          that supports equivalent copying facilities, provided you maintain
                          clear directions next to the object code saying where to find the
                          Corresponding Source.  Regardless of what server hosts the
                          Corresponding Source, you remain obligated to ensure that it is
                          available for as long as needed to satisfy these requirements.
                      
                          e) Convey the object code using peer-to-peer transmission, provided
                          you inform other peers where the object code and Corresponding
                          Source of the work are being offered to the general public at no
                          charge under subsection 6d.
                      
                        A separable portion of the object code, whose source code is excluded
                      from the Corresponding Source as a System Library, need not be
                      included in conveying the object code work.
                      
                        A "User Product" is either (1) a "consumer product", which means any
                      tangible personal property which is normally used for personal, family,
                      or household purposes, or (2) anything designed or sold for incorporation
                      into a dwelling.  In determining whether a product is a consumer product,
                      doubtful cases shall be resolved in favor of coverage.  For a particular
                      product received by a particular user, "normally used" refers to a
                      typical or common use of that class of product, regardless of the status
                      of the particular user or of the way in which the particular user
                      actually uses, or expects or is expected to use, the product.  A product
                      is a consumer product regardless of whether the product has substantial
                      commercial, industrial or non-consumer uses, unless such uses represent
                      the only significant mode of use of the product.
                      
                        "Installation Information" for a User Product means any methods,
                      procedures, authorization keys, or other information required to install
                      and execute modified versions of a covered work in that User Product from
                      a modified version of its Corresponding Source.  The information must
                      suffice to ensure that the continued functioning of the modified object
                      code is in no case prevented or interfered with solely because
                      modification has been made.
                      
                        If you convey an object code work under this section in, or with, or
                      specifically for use in, a User Product, and the conveying occurs as
                      part of a transaction in which the right of possession and use of the
                      User Product is transferred to the recipient in perpetuity or for a
                      fixed term (regardless of how the transaction is characterized), the
                      Corresponding Source conveyed under this section must be accompanied
                      by the Installation Information.  But this requirement does not apply
                      if neither you nor any third party retains the ability to install
                      modified object code on the User Product (for example, the work has
                      been installed in ROM).
                      
                        The requirement to provide Installation Information does not include a
                      requirement to continue to provide support service, warranty, or updates
                      for a work that has been modified or installed by the recipient, or for
                      the User Product in which it has been modified or installed.  Access to a
                      network may be denied when the modification itself materially and
                      adversely affects the operation of the network or violates the rules and
                      protocols for communication across the network.
                      
                        Corresponding Source conveyed, and Installation Information provided,
                      in accord with this section must be in a format that is publicly
                      documented (and with an implementation available to the public in
                      source code form), and must require no special password or key for
                      unpacking, reading or copying.
                      
                        7. Additional Terms.
                      
                        "Additional permissions" are terms that supplement the terms of this
                      License by making exceptions from one or more of its conditions.
                      Additional permissions that are applicable to the entire Program shall
                      be treated as though they were included in this License, to the extent
                      that they are valid under applicable law.  If additional permissions
                      apply only to part of the Program, that part may be used separately
                      under those permissions, but the entire Program remains governed by
                      this License without regard to the additional permissions.
                      
                        When you convey a copy of a covered work, you may at your option
                      remove any additional permissions from that copy, or from any part of
                      it.  (Additional permissions may be written to require their own
                      removal in certain cases when you modify the work.)  You may place
                      additional permissions on material, added by you to a covered work,
                      for which you have or can give appropriate copyright permission.
                      
                        Notwithstanding any other provision of this License, for material you
                      add to a covered work, you may (if authorized by the copyright holders of
                      that material) supplement the terms of this License with terms:
                      
                          a) Disclaiming warranty or limiting liability differently from the
                          terms of sections 15 and 16 of this License; or
                      
                          b) Requiring preservation of specified reasonable legal notices or
                          author attributions in that material or in the Appropriate Legal
                          Notices displayed by works containing it; or
                      
                          c) Prohibiting misrepresentation of the origin of that material, or
                          requiring that modified versions of such material be marked in
                          reasonable ways as different from the original version; or
                      
                          d) Limiting the use for publicity purposes of names of licensors or
                          authors of the material; or
                      
                          e) Declining to grant rights under trademark law for use of some
                          trade names, trademarks, or service marks; or
                      
                          f) Requiring indemnification of licensors and authors of that
                          material by anyone who conveys the material (or modified versions of
                          it) with contractual assumptions of liability to the recipient, for
                          any liability that these contractual assumptions directly impose on
                          those licensors and authors.
                      
                        All other non-permissive additional terms are considered "further
                      restrictions" within the meaning of section 10.  If the Program as you
                      received it, or any part of it, contains a notice stating that it is
                      governed by this License along with a term that is a further
                      restriction, you may remove that term.  If a license document contains
                      a further restriction but permits relicensing or conveying under this
                      License, you may add to a covered work material governed by the terms
                      of that license document, provided that the further restriction does
                      not survive such relicensing or conveying.
                      
                        If you add terms to a covered work in accord with this section, you
                      must place, in the relevant source files, a statement of the
                      additional terms that apply to those files, or a notice indicating
                      where to find the applicable terms.
                      
                        Additional terms, permissive or non-permissive, may be stated in the
                      form of a separately written license, or stated as exceptions;
                      the above requirements apply either way.
                      
                        8. Termination.
                      
                        You may not propagate or modify a covered work except as expressly
                      provided under this License.  Any attempt otherwise to propagate or
                      modify it is void, and will automatically terminate your rights under
                      this License (including any patent licenses granted under the third
                      paragraph of section 11).
                      
                        However, if you cease all violation of this License, then your
                      license from a particular copyright holder is reinstated (a)
                      provisionally, unless and until the copyright holder explicitly and
                      finally terminates your license, and (b) permanently, if the copyright
                      holder fails to notify you of the violation by some reasonable means
                      prior to 60 days after the cessation.
                      
                        Moreover, your license from a particular copyright holder is
                      reinstated permanently if the copyright holder notifies you of the
                      violation by some reasonable means, this is the first time you have
                      received notice of violation of this License (for any work) from that
                      copyright holder, and you cure the violation prior to 30 days after
                      your receipt of the notice.
                      
                        Termination of your rights under this section does not terminate the
                      licenses of parties who have received copies or rights from you under
                      this License.  If your rights have been terminated and not permanently
                      reinstated, you do not qualify to receive new licenses for the same
                      material under section 10.
                      
                        9. Acceptance Not Required for Having Copies.
                      
                        You are not required to accept this License in order to receive or
                      run a copy of the Program.  Ancillary propagation of a covered work
                      occurring solely as a consequence of using peer-to-peer transmission
                      to receive a copy likewise does not require acceptance.  However,
                      nothing other than this License grants you permission to propagate or
                      modify any covered work.  These actions infringe copyright if you do
                      not accept this License.  Therefore, by modifying or propagating a
                      covered work, you indicate your acceptance of this License to do so.
                      
                        10. Automatic Licensing of Downstream Recipients.
                      
                        Each time you convey a covered work, the recipient automatically
                      receives a license from the original licensors, to run, modify and
                      propagate that work, subject to this License.  You are not responsible
                      for enforcing compliance by third parties with this License.
                      
                        An "entity transaction" is a transaction transferring control of an
                      organization, or substantially all assets of one, or subdividing an
                      organization, or merging organizations.  If propagation of a covered
                      work results from an entity transaction, each party to that
                      transaction who receives a copy of the work also receives whatever
                      licenses to the work the party's predecessor in interest had or could
                      give under the previous paragraph, plus a right to possession of the
                      Corresponding Source of the work from the predecessor in interest, if
                      the predecessor has it or can get it with reasonable efforts.
                      
                        You may not impose any further restrictions on the exercise of the
                      rights granted or affirmed under this License.  For example, you may
                      not impose a license fee, royalty, or other charge for exercise of
                      rights granted under this License, and you may not initiate litigation
                      (including a cross-claim or counterclaim in a lawsuit) alleging that
                      any patent claim is infringed by making, using, selling, offering for
                      sale, or importing the Program or any portion of it.
                      
                        11. Patents.
                      
                        A "contributor" is a copyright holder who authorizes use under this
                      License of the Program or a work on which the Program is based.  The
                      work thus licensed is called the contributor's "contributor version".
                      
                        A contributor's "essential patent claims" are all patent claims
                      owned or controlled by the contributor, whether already acquired or
                      hereafter acquired, that would be infringed by some manner, permitted
                      by this License, of making, using, or selling its contributor version,
                      but do not include claims that would be infringed only as a
                      consequence of further modification of the contributor version.  For
                      purposes of this definition, "control" includes the right to grant
                      patent sublicenses in a manner consistent with the requirements of
                      this License.
                      
                        Each contributor grants you a non-exclusive, worldwide, royalty-free
                      patent license under the contributor's essential patent claims, to
                      make, use, sell, offer for sale, import and otherwise run, modify and
                      propagate the contents of its contributor version.
                      
                        In the following three paragraphs, a "patent license" is any express
                      agreement or commitment, however denominated, not to enforce a patent
                      (such as an express permission to practice a patent or covenant not to
                      sue for patent infringement).  To "grant" such a patent license to a
                      party means to make such an agreement or commitment not to enforce a
                      patent against the party.
                      
                        If you convey a covered work, knowingly relying on a patent license,
                      and the Corresponding Source of the work is not available for anyone
                      to copy, free of charge and under the terms of this License, through a
                      publicly available network server or other readily accessible means,
                      then you must either (1) cause the Corresponding Source to be so
                      available, or (2) arrange to deprive yourself of the benefit of the
                      patent license for this particular work, or (3) arrange, in a manner
                      consistent with the requirements of this License, to extend the patent
                      license to downstream recipients.  "Knowingly relying" means you have
                      actual knowledge that, but for the patent license, your conveying the
                      covered work in a country, or your recipient's use of the covered work
                      in a country, would infringe one or more identifiable patents in that
                      country that you have reason to believe are valid.
                      
                        If, pursuant to or in connection with a single transaction or
                      arrangement, you convey, or propagate by procuring conveyance of, a
                      covered work, and grant a patent license to some of the parties
                      receiving the covered work authorizing them to use, propagate, modify
                      or convey a specific copy of the covered work, then the patent license
                      you grant is automatically extended to all recipients of the covered
                      work and works based on it.
                      
                        A patent license is "discriminatory" if it does not include within
                      the scope of its coverage, prohibits the exercise of, or is
                      conditioned on the non-exercise of one or more of the rights that are
                      specifically granted under this License.  You may not convey a covered
                      work if you are a party to an arrangement with a third party that is
                      in the business of distributing software, under which you make payment
                      to the third party based on the extent of your activity of conveying
                      the work, and under which the third party grants, to any of the
                      parties who would receive the covered work from you, a discriminatory
                      patent license (a) in connection with copies of the covered work
                      conveyed by you (or copies made from those copies), or (b) primarily
                      for and in connection with specific products or compilations that
                      contain the covered work, unless you entered into that arrangement,
                      or that patent license was granted, prior to 28 March 2007.
                      
                        Nothing in this License shall be construed as excluding or limiting
                      any implied license or other defenses to infringement that may
                      otherwise be available to you under applicable patent law.
                      
                        12. No Surrender of Others' Freedom.
                      
                        If conditions are imposed on you (whether by court order, agreement or
                      otherwise) that contradict the conditions of this License, they do not
                      excuse you from the conditions of this License.  If you cannot convey a
                      covered work so as to satisfy simultaneously your obligations under this
                      License and any other pertinent obligations, then as a consequence you may
                      not convey it at all.  For example, if you agree to terms that obligate you
                      to collect a royalty for further conveying from those to whom you convey
                      the Program, the only way you could satisfy both those terms and this
                      License would be to refrain entirely from conveying the Program.
                      
                        13. Use with the GNU Affero General Public License.
                      
                        Notwithstanding any other provision of this License, you have
                      permission to link or combine any covered work with a work licensed
                      under version 3 of the GNU Affero General Public License into a single
                      combined work, and to convey the resulting work.  The terms of this
                      License will continue to apply to the part which is the covered work,
                      but the special requirements of the GNU Affero General Public License,
                      section 13, concerning interaction through a network will apply to the
                      combination as such.
                      
                        14. Revised Versions of this License.
                      
                        The Free Software Foundation may publish revised and/or new versions of
                      the GNU General Public License from time to time.  Such new versions will
                      be similar in spirit to the present version, but may differ in detail to
                      address new problems or concerns.
                      
                        Each version is given a distinguishing version number.  If the
                      Program specifies that a certain numbered version of the GNU General
                      Public License "or any later version" applies to it, you have the
                      option of following the terms and conditions either of that numbered
                      version or of any later version published by the Free Software
                      Foundation.  If the Program does not specify a version number of the
                      GNU General Public License, you may choose any version ever published
                      by the Free Software Foundation.
                      
                        If the Program specifies that a proxy can decide which future
                      versions of the GNU General Public License can be used, that proxy's
                      public statement of acceptance of a version permanently authorizes you
                      to choose that version for the Program.
                      
                        Later license versions may give you additional or different
                      permissions.  However, no additional obligations are imposed on any
                      author or copyright holder as a result of your choosing to follow a
                      later version.
                      
                        15. Disclaimer of Warranty.
                      
                        THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
                      APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
                      HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
                      OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
                      THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                      PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
                      IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
                      ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
                      
                        16. Limitation of Liability.
                      
                        IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
                      WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
                      THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
                      GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
                      USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
                      DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
                      PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
                      EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
                      SUCH DAMAGES.
                      
                        17. Interpretation of Sections 15 and 16.
                      
                        If the disclaimer of warranty and limitation of liability provided
                      above cannot be given local legal effect according to their terms,
                      reviewing courts shall apply local law that most closely approximates
                      an absolute waiver of all civil liability in connection with the
                      Program, unless a warranty or assumption of liability accompanies a
                      copy of the Program in return for a fee.
                      
                                           END OF TERMS AND CONDITIONS
                      
                                  How to Apply These Terms to Your New Programs
                      
                        If you develop a new program, and you want it to be of the greatest
                      possible use to the public, the best way to achieve this is to make it
                      free software which everyone can redistribute and change under these terms.
                      
                        To do so, attach the following notices to the program.  It is safest
                      to attach them to the start of each source file to most effectively
                      state the exclusion of warranty; and each file should have at least
                      the "copyright" line and a pointer to where the full notice is found.
                      
                          <one line to give the program's name and a brief idea of what it does.>
                          Copyright (C) <year>  <name of author>
                      
                          This program is free software: you can redistribute it and/or modify
                          it under the terms of the GNU General Public License as published by
                          the Free Software Foundation, either version 3 of the License, or
                          (at your option) any later version.
                      
                          This program is distributed in the hope that it will be useful,
                          but WITHOUT ANY WARRANTY; without even the implied warranty of
                          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                          GNU General Public License for more details.
                      
                          You should have received a copy of the GNU General Public License
                          along with this program.  If not, see <http://www.gnu.org/licenses/>.
                      
                      Also add information on how to contact you by electronic and paper mail.
                      
                        If the program does terminal interaction, make it output a short
                      notice like this when it starts in an interactive mode:
                      
                          <program>  Copyright (C) <year>  <name of author>
                          This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
                          This is free software, and you are welcome to redistribute it
                          under certain conditions; type `show c' for details.
                      
                      The hypothetical commands `show w' and `show c' should show the appropriate
                      parts of the General Public License.  Of course, your program's commands
                      might be different; for a GUI interface, you would use an "about box".
                      
                        You should also get your employer (if you work as a programmer) or school,
                      if any, to sign a "copyright disclaimer" for the program, if necessary.
                      For more information on this, and how to apply and follow the GNU GPL, see
                      <http://www.gnu.org/licenses/>.
                      
                        The GNU General Public License does not permit incorporating your program
                      into proprietary programs.  If your program is a subroutine library, you
                      may consider it more useful to permit linking proprietary applications with
                      the library.  If this is what you want to do, use the GNU Lesser General
                      Public License instead of this License.  But first, please read
                      <http://www.gnu.org/philosophy/why-not-lgpl.html>.
                      
                      */

                      File 5 of 12: FlashWallet
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
                      import "./migrations/LibBootstrap.sol";
                      import "./features/Bootstrap.sol";
                      import "./storage/LibProxyStorage.sol";
                      import "./errors/LibProxyRichErrors.sol";
                      /// @dev An extensible proxy contract that serves as a universal entry point for
                      ///      interacting with the 0x protocol.
                      contract ZeroEx {
                          // solhint-disable separate-by-one-line-in-contract,indent,var-name-mixedcase
                          using LibBytesV06 for bytes;
                          /// @dev Construct this contract and register the `Bootstrap` feature.
                          ///      After constructing this contract, `bootstrap()` should be called
                          ///      by `bootstrap()` to seed the initial feature set.
                          /// @param bootstrapper Who can call `bootstrap()`.
                          constructor(address bootstrapper) public {
                              // Temporarily create and register the bootstrap feature.
                              // It will deregister itself after `bootstrap()` has been called.
                              Bootstrap bootstrap = new Bootstrap(bootstrapper);
                              LibProxyStorage.getStorage().impls[bootstrap.bootstrap.selector] =
                                  address(bootstrap);
                          }
                          // solhint-disable state-visibility
                          /// @dev Forwards calls to the appropriate implementation contract.
                          fallback() external payable {
                              bytes4 selector = msg.data.readBytes4(0);
                              address impl = getFunctionImplementation(selector);
                              if (impl == address(0)) {
                                  _revertWithData(LibProxyRichErrors.NotImplementedError(selector));
                              }
                              (bool success, bytes memory resultData) = impl.delegatecall(msg.data);
                              if (!success) {
                                  _revertWithData(resultData);
                              }
                              _returnWithData(resultData);
                          }
                          /// @dev Fallback for just receiving ether.
                          receive() external payable {}
                          // solhint-enable state-visibility
                          /// @dev Get the implementation contract of a registered function.
                          /// @param selector The function selector.
                          /// @return impl The implementation contract address.
                          function getFunctionImplementation(bytes4 selector)
                              public
                              view
                              returns (address impl)
                          {
                              return LibProxyStorage.getStorage().impls[selector];
                          }
                          /// @dev Revert with arbitrary bytes.
                          /// @param data Revert data.
                          function _revertWithData(bytes memory data) private pure {
                              assembly { revert(add(data, 32), mload(data)) }
                          }
                          /// @dev Return with arbitrary bytes.
                          /// @param data Return data.
                          function _returnWithData(bytes memory data) private pure {
                              assembly { return(add(data, 32), mload(data)) }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./errors/LibBytesRichErrorsV06.sol";
                      import "./errors/LibRichErrorsV06.sol";
                      library LibBytesV06 {
                          using LibBytesV06 for bytes;
                          /// @dev Gets the memory address for a byte array.
                          /// @param input Byte array to lookup.
                          /// @return memoryAddress Memory address of byte array. This
                          ///         points to the header of the byte array which contains
                          ///         the length.
                          function rawAddress(bytes memory input)
                              internal
                              pure
                              returns (uint256 memoryAddress)
                          {
                              assembly {
                                  memoryAddress := input
                              }
                              return memoryAddress;
                          }
                          /// @dev Gets the memory address for the contents of a byte array.
                          /// @param input Byte array to lookup.
                          /// @return memoryAddress Memory address of the contents of the byte array.
                          function contentAddress(bytes memory input)
                              internal
                              pure
                              returns (uint256 memoryAddress)
                          {
                              assembly {
                                  memoryAddress := add(input, 32)
                              }
                              return memoryAddress;
                          }
                          /// @dev Copies `length` bytes from memory location `source` to `dest`.
                          /// @param dest memory address to copy bytes to.
                          /// @param source memory address to copy bytes from.
                          /// @param length number of bytes to copy.
                          function memCopy(
                              uint256 dest,
                              uint256 source,
                              uint256 length
                          )
                              internal
                              pure
                          {
                              if (length < 32) {
                                  // Handle a partial word by reading destination and masking
                                  // off the bits we are interested in.
                                  // This correctly handles overlap, zero lengths and source == dest
                                  assembly {
                                      let mask := sub(exp(256, sub(32, length)), 1)
                                      let s := and(mload(source), not(mask))
                                      let d := and(mload(dest), mask)
                                      mstore(dest, or(s, d))
                                  }
                              } else {
                                  // Skip the O(length) loop when source == dest.
                                  if (source == dest) {
                                      return;
                                  }
                                  // For large copies we copy whole words at a time. The final
                                  // word is aligned to the end of the range (instead of after the
                                  // previous) to handle partial words. So a copy will look like this:
                                  //
                                  //  ####
                                  //      ####
                                  //          ####
                                  //            ####
                                  //
                                  // We handle overlap in the source and destination range by
                                  // changing the copying direction. This prevents us from
                                  // overwriting parts of source that we still need to copy.
                                  //
                                  // This correctly handles source == dest
                                  //
                                  if (source > dest) {
                                      assembly {
                                          // We subtract 32 from `sEnd` and `dEnd` because it
                                          // is easier to compare with in the loop, and these
                                          // are also the addresses we need for copying the
                                          // last bytes.
                                          length := sub(length, 32)
                                          let sEnd := add(source, length)
                                          let dEnd := add(dest, length)
                                          // Remember the last 32 bytes of source
                                          // This needs to be done here and not after the loop
                                          // because we may have overwritten the last bytes in
                                          // source already due to overlap.
                                          let last := mload(sEnd)
                                          // Copy whole words front to back
                                          // Note: the first check is always true,
                                          // this could have been a do-while loop.
                                          // solhint-disable-next-line no-empty-blocks
                                          for {} lt(source, sEnd) {} {
                                              mstore(dest, mload(source))
                                              source := add(source, 32)
                                              dest := add(dest, 32)
                                          }
                                          // Write the last 32 bytes
                                          mstore(dEnd, last)
                                      }
                                  } else {
                                      assembly {
                                          // We subtract 32 from `sEnd` and `dEnd` because those
                                          // are the starting points when copying a word at the end.
                                          length := sub(length, 32)
                                          let sEnd := add(source, length)
                                          let dEnd := add(dest, length)
                                          // Remember the first 32 bytes of source
                                          // This needs to be done here and not after the loop
                                          // because we may have overwritten the first bytes in
                                          // source already due to overlap.
                                          let first := mload(source)
                                          // Copy whole words back to front
                                          // We use a signed comparisson here to allow dEnd to become
                                          // negative (happens when source and dest < 32). Valid
                                          // addresses in local memory will never be larger than
                                          // 2**255, so they can be safely re-interpreted as signed.
                                          // Note: the first check is always true,
                                          // this could have been a do-while loop.
                                          // solhint-disable-next-line no-empty-blocks
                                          for {} slt(dest, dEnd) {} {
                                              mstore(dEnd, mload(sEnd))
                                              sEnd := sub(sEnd, 32)
                                              dEnd := sub(dEnd, 32)
                                          }
                                          // Write the first 32 bytes
                                          mstore(dest, first)
                                      }
                                  }
                              }
                          }
                          /// @dev Returns a slices from a byte array.
                          /// @param b The byte array to take a slice from.
                          /// @param from The starting index for the slice (inclusive).
                          /// @param to The final index for the slice (exclusive).
                          /// @return result The slice containing bytes at indices [from, to)
                          function slice(
                              bytes memory b,
                              uint256 from,
                              uint256 to
                          )
                              internal
                              pure
                              returns (bytes memory result)
                          {
                              // Ensure that the from and to positions are valid positions for a slice within
                              // the byte array that is being used.
                              if (from > to) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
                                      from,
                                      to
                                  ));
                              }
                              if (to > b.length) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
                                      to,
                                      b.length
                                  ));
                              }
                              // Create a new bytes structure and copy contents
                              result = new bytes(to - from);
                              memCopy(
                                  result.contentAddress(),
                                  b.contentAddress() + from,
                                  result.length
                              );
                              return result;
                          }
                          /// @dev Returns a slice from a byte array without preserving the input.
                          ///      When `from == 0`, the original array will match the slice.
                          ///      In other cases its state will be corrupted.
                          /// @param b The byte array to take a slice from. Will be destroyed in the process.
                          /// @param from The starting index for the slice (inclusive).
                          /// @param to The final index for the slice (exclusive).
                          /// @return result The slice containing bytes at indices [from, to)
                          function sliceDestructive(
                              bytes memory b,
                              uint256 from,
                              uint256 to
                          )
                              internal
                              pure
                              returns (bytes memory result)
                          {
                              // Ensure that the from and to positions are valid positions for a slice within
                              // the byte array that is being used.
                              if (from > to) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
                                      from,
                                      to
                                  ));
                              }
                              if (to > b.length) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
                                      to,
                                      b.length
                                  ));
                              }
                              // Create a new bytes structure around [from, to) in-place.
                              assembly {
                                  result := add(b, from)
                                  mstore(result, sub(to, from))
                              }
                              return result;
                          }
                          /// @dev Pops the last byte off of a byte array by modifying its length.
                          /// @param b Byte array that will be modified.
                          /// @return result The byte that was popped off.
                          function popLastByte(bytes memory b)
                              internal
                              pure
                              returns (bytes1 result)
                          {
                              if (b.length == 0) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanZeroRequired,
                                      b.length,
                                      0
                                  ));
                              }
                              // Store last byte.
                              result = b[b.length - 1];
                              assembly {
                                  // Decrement length of byte array.
                                  let newLen := sub(mload(b), 1)
                                  mstore(b, newLen)
                              }
                              return result;
                          }
                          /// @dev Tests equality of two byte arrays.
                          /// @param lhs First byte array to compare.
                          /// @param rhs Second byte array to compare.
                          /// @return equal True if arrays are the same. False otherwise.
                          function equals(
                              bytes memory lhs,
                              bytes memory rhs
                          )
                              internal
                              pure
                              returns (bool equal)
                          {
                              // Keccak gas cost is 30 + numWords * 6. This is a cheap way to compare.
                              // We early exit on unequal lengths, but keccak would also correctly
                              // handle this.
                              return lhs.length == rhs.length && keccak256(lhs) == keccak256(rhs);
                          }
                          /// @dev Reads an address from a position in a byte array.
                          /// @param b Byte array containing an address.
                          /// @param index Index in byte array of address.
                          /// @return result address from byte array.
                          function readAddress(
                              bytes memory b,
                              uint256 index
                          )
                              internal
                              pure
                              returns (address result)
                          {
                              if (b.length < index + 20) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
                                      b.length,
                                      index + 20 // 20 is length of address
                                  ));
                              }
                              // Add offset to index:
                              // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
                              // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
                              index += 20;
                              // Read address from array memory
                              assembly {
                                  // 1. Add index to address of bytes array
                                  // 2. Load 32-byte word from memory
                                  // 3. Apply 20-byte mask to obtain address
                                  result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff)
                              }
                              return result;
                          }
                          /// @dev Writes an address into a specific position in a byte array.
                          /// @param b Byte array to insert address into.
                          /// @param index Index in byte array of address.
                          /// @param input Address to put into byte array.
                          function writeAddress(
                              bytes memory b,
                              uint256 index,
                              address input
                          )
                              internal
                              pure
                          {
                              if (b.length < index + 20) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
                                      b.length,
                                      index + 20 // 20 is length of address
                                  ));
                              }
                              // Add offset to index:
                              // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
                              // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
                              index += 20;
                              // Store address into array memory
                              assembly {
                                  // The address occupies 20 bytes and mstore stores 32 bytes.
                                  // First fetch the 32-byte word where we'll be storing the address, then
                                  // apply a mask so we have only the bytes in the word that the address will not occupy.
                                  // Then combine these bytes with the address and store the 32 bytes back to memory with mstore.
                                  // 1. Add index to address of bytes array
                                  // 2. Load 32-byte word from memory
                                  // 3. Apply 12-byte mask to obtain extra bytes occupying word of memory where we'll store the address
                                  let neighbors := and(
                                      mload(add(b, index)),
                                      0xffffffffffffffffffffffff0000000000000000000000000000000000000000
                                  )
                                  // Make sure input address is clean.
                                  // (Solidity does not guarantee this)
                                  input := and(input, 0xffffffffffffffffffffffffffffffffffffffff)
                                  // Store the neighbors and address into memory
                                  mstore(add(b, index), xor(input, neighbors))
                              }
                          }
                          /// @dev Reads a bytes32 value from a position in a byte array.
                          /// @param b Byte array containing a bytes32 value.
                          /// @param index Index in byte array of bytes32 value.
                          /// @return result bytes32 value from byte array.
                          function readBytes32(
                              bytes memory b,
                              uint256 index
                          )
                              internal
                              pure
                              returns (bytes32 result)
                          {
                              if (b.length < index + 32) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
                                      b.length,
                                      index + 32
                                  ));
                              }
                              // Arrays are prefixed by a 256 bit length parameter
                              index += 32;
                              // Read the bytes32 from array memory
                              assembly {
                                  result := mload(add(b, index))
                              }
                              return result;
                          }
                          /// @dev Writes a bytes32 into a specific position in a byte array.
                          /// @param b Byte array to insert <input> into.
                          /// @param index Index in byte array of <input>.
                          /// @param input bytes32 to put into byte array.
                          function writeBytes32(
                              bytes memory b,
                              uint256 index,
                              bytes32 input
                          )
                              internal
                              pure
                          {
                              if (b.length < index + 32) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
                                      b.length,
                                      index + 32
                                  ));
                              }
                              // Arrays are prefixed by a 256 bit length parameter
                              index += 32;
                              // Read the bytes32 from array memory
                              assembly {
                                  mstore(add(b, index), input)
                              }
                          }
                          /// @dev Reads a uint256 value from a position in a byte array.
                          /// @param b Byte array containing a uint256 value.
                          /// @param index Index in byte array of uint256 value.
                          /// @return result uint256 value from byte array.
                          function readUint256(
                              bytes memory b,
                              uint256 index
                          )
                              internal
                              pure
                              returns (uint256 result)
                          {
                              result = uint256(readBytes32(b, index));
                              return result;
                          }
                          /// @dev Writes a uint256 into a specific position in a byte array.
                          /// @param b Byte array to insert <input> into.
                          /// @param index Index in byte array of <input>.
                          /// @param input uint256 to put into byte array.
                          function writeUint256(
                              bytes memory b,
                              uint256 index,
                              uint256 input
                          )
                              internal
                              pure
                          {
                              writeBytes32(b, index, bytes32(input));
                          }
                          /// @dev Reads an unpadded bytes4 value from a position in a byte array.
                          /// @param b Byte array containing a bytes4 value.
                          /// @param index Index in byte array of bytes4 value.
                          /// @return result bytes4 value from byte array.
                          function readBytes4(
                              bytes memory b,
                              uint256 index
                          )
                              internal
                              pure
                              returns (bytes4 result)
                          {
                              if (b.length < index + 4) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
                                      b.length,
                                      index + 4
                                  ));
                              }
                              // Arrays are prefixed by a 32 byte length field
                              index += 32;
                              // Read the bytes4 from array memory
                              assembly {
                                  result := mload(add(b, index))
                                  // Solidity does not require us to clean the trailing bytes.
                                  // We do it anyway
                                  result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
                              }
                              return result;
                          }
                          /// @dev Writes a new length to a byte array.
                          ///      Decreasing length will lead to removing the corresponding lower order bytes from the byte array.
                          ///      Increasing length may lead to appending adjacent in-memory bytes to the end of the byte array.
                          /// @param b Bytes array to write new length to.
                          /// @param length New length of byte array.
                          function writeLength(bytes memory b, uint256 length)
                              internal
                              pure
                          {
                              assembly {
                                  mstore(b, length)
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibBytesRichErrorsV06 {
                          enum InvalidByteOperationErrorCodes {
                              FromLessThanOrEqualsToRequired,
                              ToLessThanOrEqualsLengthRequired,
                              LengthGreaterThanZeroRequired,
                              LengthGreaterThanOrEqualsFourRequired,
                              LengthGreaterThanOrEqualsTwentyRequired,
                              LengthGreaterThanOrEqualsThirtyTwoRequired,
                              LengthGreaterThanOrEqualsNestedBytesLengthRequired,
                              DestinationLengthGreaterThanOrEqualSourceLengthRequired
                          }
                          // bytes4(keccak256("InvalidByteOperationError(uint8,uint256,uint256)"))
                          bytes4 internal constant INVALID_BYTE_OPERATION_ERROR_SELECTOR =
                              0x28006595;
                          // solhint-disable func-name-mixedcase
                          function InvalidByteOperationError(
                              InvalidByteOperationErrorCodes errorCode,
                              uint256 offset,
                              uint256 required
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  INVALID_BYTE_OPERATION_ERROR_SELECTOR,
                                  errorCode,
                                  offset,
                                  required
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibRichErrorsV06 {
                          // bytes4(keccak256("Error(string)"))
                          bytes4 internal constant STANDARD_ERROR_SELECTOR = 0x08c379a0;
                          // solhint-disable func-name-mixedcase
                          /// @dev ABI encode a standard, string revert error payload.
                          ///      This is the same payload that would be included by a `revert(string)`
                          ///      solidity statement. It has the function signature `Error(string)`.
                          /// @param message The error string.
                          /// @return The ABI encoded error.
                          function StandardError(string memory message)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  STANDARD_ERROR_SELECTOR,
                                  bytes(message)
                              );
                          }
                          // solhint-enable func-name-mixedcase
                          /// @dev Reverts an encoded rich revert reason `errorData`.
                          /// @param errorData ABI encoded error data.
                          function rrevert(bytes memory errorData)
                              internal
                              pure
                          {
                              assembly {
                                  revert(add(errorData, 0x20), mload(errorData))
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../errors/LibProxyRichErrors.sol";
                      library LibBootstrap {
                          /// @dev Magic bytes returned by the bootstrapper to indicate success.
                          ///      This is `keccack('BOOTSTRAP_SUCCESS')`.
                          bytes4 internal constant BOOTSTRAP_SUCCESS = 0xd150751b;
                          using LibRichErrorsV06 for bytes;
                          /// @dev Perform a delegatecall and ensure it returns the magic bytes.
                          /// @param target The call target.
                          /// @param data The call data.
                          function delegatecallBootstrapFunction(
                              address target,
                              bytes memory data
                          )
                              internal
                          {
                              (bool success, bytes memory resultData) = target.delegatecall(data);
                              if (!success ||
                                  resultData.length != 32 ||
                                  abi.decode(resultData, (bytes4)) != BOOTSTRAP_SUCCESS)
                              {
                                  LibProxyRichErrors.BootstrapCallFailedError(target, resultData).rrevert();
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibProxyRichErrors {
                          // solhint-disable func-name-mixedcase
                          function NotImplementedError(bytes4 selector)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("NotImplementedError(bytes4)")),
                                  selector
                              );
                          }
                          function InvalidBootstrapCallerError(address actual, address expected)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InvalidBootstrapCallerError(address,address)")),
                                  actual,
                                  expected
                              );
                          }
                          function InvalidDieCallerError(address actual, address expected)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InvalidDieCallerError(address,address)")),
                                  actual,
                                  expected
                              );
                          }
                          function BootstrapCallFailedError(address target, bytes memory resultData)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("BootstrapCallFailedError(address,bytes)")),
                                  target,
                                  resultData
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../migrations/LibBootstrap.sol";
                      import "../storage/LibProxyStorage.sol";
                      import "./IBootstrap.sol";
                      /// @dev Detachable `bootstrap()` feature.
                      contract Bootstrap is
                          IBootstrap
                      {
                          // solhint-disable state-visibility,indent
                          /// @dev The ZeroEx contract.
                          ///      This has to be immutable to persist across delegatecalls.
                          address immutable private _deployer;
                          /// @dev The implementation address of this contract.
                          ///      This has to be immutable to persist across delegatecalls.
                          address immutable private _implementation;
                          /// @dev The deployer.
                          ///      This has to be immutable to persist across delegatecalls.
                          address immutable private _bootstrapCaller;
                          // solhint-enable state-visibility,indent
                          using LibRichErrorsV06 for bytes;
                          /// @dev Construct this contract and set the bootstrap migration contract.
                          ///      After constructing this contract, `bootstrap()` should be called
                          ///      to seed the initial feature set.
                          /// @param bootstrapCaller The allowed caller of `bootstrap()`.
                          constructor(address bootstrapCaller) public {
                              _deployer = msg.sender;
                              _implementation = address(this);
                              _bootstrapCaller = bootstrapCaller;
                          }
                          /// @dev Bootstrap the initial feature set of this contract by delegatecalling
                          ///      into `target`. Before exiting the `bootstrap()` function will
                          ///      deregister itself from the proxy to prevent being called again.
                          /// @param target The bootstrapper contract address.
                          /// @param callData The call data to execute on `target`.
                          function bootstrap(address target, bytes calldata callData) external override {
                              // Only the bootstrap caller can call this function.
                              if (msg.sender != _bootstrapCaller) {
                                  LibProxyRichErrors.InvalidBootstrapCallerError(
                                      msg.sender,
                                      _bootstrapCaller
                                  ).rrevert();
                              }
                              // Deregister.
                              LibProxyStorage.getStorage().impls[this.bootstrap.selector] = address(0);
                              // Self-destruct.
                              Bootstrap(_implementation).die();
                              // Call the bootstrapper.
                              LibBootstrap.delegatecallBootstrapFunction(target, callData);
                          }
                          /// @dev Self-destructs this contract.
                          ///      Can only be called by the deployer.
                          function die() external {
                              if (msg.sender != _deployer) {
                                  LibProxyRichErrors.InvalidDieCallerError(msg.sender, _deployer).rrevert();
                              }
                              selfdestruct(msg.sender);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./LibStorage.sol";
                      /// @dev Storage helpers for the proxy contract.
                      library LibProxyStorage {
                          /// @dev Storage bucket for proxy contract.
                          struct Storage {
                              // Mapping of function selector -> function implementation
                              mapping(bytes4 => address) impls;
                              // The owner of the proxy contract.
                              address owner;
                          }
                          /// @dev Get the storage bucket for this contract.
                          function getStorage() internal pure returns (Storage storage stor) {
                              uint256 storageSlot = LibStorage.getStorageSlot(
                                  LibStorage.StorageId.Proxy
                              );
                              // Dip into assembly to change the slot pointed to by the local
                              // variable `stor`.
                              // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
                              assembly { stor_slot := storageSlot }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      /// @dev Common storage helpers
                      library LibStorage {
                          /// @dev What to bit-shift a storage ID by to get its slot.
                          ///      This gives us a maximum of 2**128 inline fields in each bucket.
                          uint256 private constant STORAGE_SLOT_EXP = 128;
                          /// @dev Storage IDs for feature storage buckets.
                          ///      WARNING: APPEND-ONLY.
                          enum StorageId {
                              Proxy,
                              SimpleFunctionRegistry,
                              Ownable,
                              TokenSpender,
                              TransformERC20,
                              MetaTransactions
                          }
                          /// @dev Get the storage slot given a storage ID. We assign unique, well-spaced
                          ///     slots to storage bucket variables to ensure they do not overlap.
                          ///     See: https://solidity.readthedocs.io/en/v0.6.6/assembly.html#access-to-external-variables-functions-and-libraries
                          /// @param storageId An entry in `StorageId`
                          /// @return slot The storage slot.
                          function getStorageSlot(StorageId storageId)
                              internal
                              pure
                              returns (uint256 slot)
                          {
                              // This should never overflow with a reasonable `STORAGE_SLOT_EXP`
                              // because Solidity will do a range check on `storageId` during the cast.
                              return (uint256(storageId) + 1) << STORAGE_SLOT_EXP;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      /// @dev Detachable `bootstrap()` feature.
                      interface IBootstrap {
                          /// @dev Bootstrap the initial feature set of this contract by delegatecalling
                          ///      into `target`. Before exiting the `bootstrap()` function will
                          ///      deregister itself from the proxy to prevent being called again.
                          /// @param target The bootstrapper contract address.
                          /// @param callData The call data to execute on `target`.
                          function bootstrap(address target, bytes calldata callData) external;
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibCommonRichErrors {
                          // solhint-disable func-name-mixedcase
                          function OnlyCallableBySelfError(address sender)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("OnlyCallableBySelfError(address)")),
                                  sender
                              );
                          }
                          function IllegalReentrancyError()
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("IllegalReentrancyError()"))
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibMetaTransactionsRichErrors {
                          // solhint-disable func-name-mixedcase
                          function InvalidMetaTransactionsArrayLengthsError(
                              uint256 mtxCount,
                              uint256 signatureCount
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InvalidMetaTransactionsArrayLengthsError(uint256,uint256)")),
                                  mtxCount,
                                  signatureCount
                              );
                          }
                          function MetaTransactionUnsupportedFunctionError(
                              bytes32 mtxHash,
                              bytes4 selector
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("MetaTransactionUnsupportedFunctionError(bytes32,bytes4)")),
                                  mtxHash,
                                  selector
                              );
                          }
                          function MetaTransactionWrongSenderError(
                              bytes32 mtxHash,
                              address sender,
                              address expectedSender
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("MetaTransactionWrongSenderError(bytes32,address,address)")),
                                  mtxHash,
                                  sender,
                                  expectedSender
                              );
                          }
                          function MetaTransactionExpiredError(
                              bytes32 mtxHash,
                              uint256 time,
                              uint256 expirationTime
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("MetaTransactionExpiredError(bytes32,uint256,uint256)")),
                                  mtxHash,
                                  time,
                                  expirationTime
                              );
                          }
                          function MetaTransactionGasPriceError(
                              bytes32 mtxHash,
                              uint256 gasPrice,
                              uint256 minGasPrice,
                              uint256 maxGasPrice
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("MetaTransactionGasPriceError(bytes32,uint256,uint256,uint256)")),
                                  mtxHash,
                                  gasPrice,
                                  minGasPrice,
                                  maxGasPrice
                              );
                          }
                          function MetaTransactionInsufficientEthError(
                              bytes32 mtxHash,
                              uint256 ethBalance,
                              uint256 ethRequired
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("MetaTransactionInsufficientEthError(bytes32,uint256,uint256)")),
                                  mtxHash,
                                  ethBalance,
                                  ethRequired
                              );
                          }
                          function MetaTransactionInvalidSignatureError(
                              bytes32 mtxHash,
                              bytes memory signature,
                              bytes memory errData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("MetaTransactionInvalidSignatureError(bytes32,bytes,bytes)")),
                                  mtxHash,
                                  signature,
                                  errData
                              );
                          }
                          function MetaTransactionAlreadyExecutedError(
                              bytes32 mtxHash,
                              uint256 executedBlockNumber
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("MetaTransactionAlreadyExecutedError(bytes32,uint256)")),
                                  mtxHash,
                                  executedBlockNumber
                              );
                          }
                          function MetaTransactionCallFailedError(
                              bytes32 mtxHash,
                              bytes memory callData,
                              bytes memory returnData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("MetaTransactionCallFailedError(bytes32,bytes,bytes)")),
                                  mtxHash,
                                  callData,
                                  returnData
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibOwnableRichErrors {
                          // solhint-disable func-name-mixedcase
                          function OnlyOwnerError(
                              address sender,
                              address owner
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("OnlyOwnerError(address,address)")),
                                  sender,
                                  owner
                              );
                          }
                          function TransferOwnerToZeroError()
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("TransferOwnerToZeroError()"))
                              );
                          }
                          function MigrateCallFailedError(address target, bytes memory resultData)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("MigrateCallFailedError(address,bytes)")),
                                  target,
                                  resultData
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibSignatureRichErrors {
                          enum SignatureValidationErrorCodes {
                              ALWAYS_INVALID,
                              INVALID_LENGTH,
                              UNSUPPORTED,
                              ILLEGAL,
                              WRONG_SIGNER
                          }
                          // solhint-disable func-name-mixedcase
                          function SignatureValidationError(
                              SignatureValidationErrorCodes code,
                              bytes32 hash,
                              address signerAddress,
                              bytes memory signature
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("SignatureValidationError(uint8,bytes32,address,bytes)")),
                                  code,
                                  hash,
                                  signerAddress,
                                  signature
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibSimpleFunctionRegistryRichErrors {
                          // solhint-disable func-name-mixedcase
                          function NotInRollbackHistoryError(bytes4 selector, address targetImpl)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("NotInRollbackHistoryError(bytes4,address)")),
                                  selector,
                                  targetImpl
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibSpenderRichErrors {
                          // solhint-disable func-name-mixedcase
                          function SpenderERC20TransferFromFailedError(
                              address token,
                              address owner,
                              address to,
                              uint256 amount,
                              bytes memory errorData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("SpenderERC20TransferFromFailedError(address,address,address,uint256,bytes)")),
                                  token,
                                  owner,
                                  to,
                                  amount,
                                  errorData
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibTransformERC20RichErrors {
                          // solhint-disable func-name-mixedcase,separate-by-one-line-in-contract
                          function InsufficientEthAttachedError(
                              uint256 ethAttached,
                              uint256 ethNeeded
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InsufficientEthAttachedError(uint256,uint256)")),
                                  ethAttached,
                                  ethNeeded
                              );
                          }
                          function IncompleteTransformERC20Error(
                              address outputToken,
                              uint256 outputTokenAmount,
                              uint256 minOutputTokenAmount
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("IncompleteTransformERC20Error(address,uint256,uint256)")),
                                  outputToken,
                                  outputTokenAmount,
                                  minOutputTokenAmount
                              );
                          }
                          function NegativeTransformERC20OutputError(
                              address outputToken,
                              uint256 outputTokenLostAmount
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("NegativeTransformERC20OutputError(address,uint256)")),
                                  outputToken,
                                  outputTokenLostAmount
                              );
                          }
                          function TransformerFailedError(
                              address transformer,
                              bytes memory transformerData,
                              bytes memory resultData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("TransformerFailedError(address,bytes,bytes)")),
                                  transformer,
                                  transformerData,
                                  resultData
                              );
                          }
                          // Common Transformer errors ///////////////////////////////////////////////
                          function OnlyCallableByDeployerError(
                              address caller,
                              address deployer
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("OnlyCallableByDeployerError(address,address)")),
                                  caller,
                                  deployer
                              );
                          }
                          function InvalidExecutionContextError(
                              address actualContext,
                              address expectedContext
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InvalidExecutionContextError(address,address)")),
                                  actualContext,
                                  expectedContext
                              );
                          }
                          enum InvalidTransformDataErrorCode {
                              INVALID_TOKENS,
                              INVALID_ARRAY_LENGTH
                          }
                          function InvalidTransformDataError(
                              InvalidTransformDataErrorCode errorCode,
                              bytes memory transformData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InvalidTransformDataError(uint8,bytes)")),
                                  errorCode,
                                  transformData
                              );
                          }
                          // FillQuoteTransformer errors /////////////////////////////////////////////
                          function IncompleteFillSellQuoteError(
                              address sellToken,
                              uint256 soldAmount,
                              uint256 sellAmount
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("IncompleteFillSellQuoteError(address,uint256,uint256)")),
                                  sellToken,
                                  soldAmount,
                                  sellAmount
                              );
                          }
                          function IncompleteFillBuyQuoteError(
                              address buyToken,
                              uint256 boughtAmount,
                              uint256 buyAmount
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("IncompleteFillBuyQuoteError(address,uint256,uint256)")),
                                  buyToken,
                                  boughtAmount,
                                  buyAmount
                              );
                          }
                          function InsufficientTakerTokenError(
                              uint256 tokenBalance,
                              uint256 tokensNeeded
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InsufficientTakerTokenError(uint256,uint256)")),
                                  tokenBalance,
                                  tokensNeeded
                              );
                          }
                          function InsufficientProtocolFeeError(
                              uint256 ethBalance,
                              uint256 ethNeeded
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InsufficientProtocolFeeError(uint256,uint256)")),
                                  ethBalance,
                                  ethNeeded
                              );
                          }
                          function InvalidERC20AssetDataError(
                              bytes memory assetData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InvalidERC20AssetDataError(bytes)")),
                                  assetData
                              );
                          }
                          function InvalidTakerFeeTokenError(
                              address token
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InvalidTakerFeeTokenError(address)")),
                                  token
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibWalletRichErrors {
                          // solhint-disable func-name-mixedcase
                          function WalletExecuteCallFailedError(
                              address wallet,
                              address callTarget,
                              bytes memory callData,
                              uint256 callValue,
                              bytes memory errorData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("WalletExecuteCallFailedError(address,address,bytes,uint256,bytes)")),
                                  wallet,
                                  callTarget,
                                  callData,
                                  callValue,
                                  errorData
                              );
                          }
                          function WalletExecuteDelegateCallFailedError(
                              address wallet,
                              address callTarget,
                              bytes memory callData,
                              bytes memory errorData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("WalletExecuteDelegateCallFailedError(address,address,bytes,bytes)")),
                                  wallet,
                                  callTarget,
                                  callData,
                                  errorData
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/AuthorizableV06.sol";
                      import "../errors/LibSpenderRichErrors.sol";
                      import "./IAllowanceTarget.sol";
                      /// @dev The allowance target for the TokenSpender feature.
                      contract AllowanceTarget is
                          IAllowanceTarget,
                          AuthorizableV06
                      {
                          // solhint-disable no-unused-vars,indent,no-empty-blocks
                          using LibRichErrorsV06 for bytes;
                          /// @dev Execute an arbitrary call. Only an authority can call this.
                          /// @param target The call target.
                          /// @param callData The call data.
                          /// @return resultData The data returned by the call.
                          function executeCall(
                              address payable target,
                              bytes calldata callData
                          )
                              external
                              override
                              onlyAuthorized
                              returns (bytes memory resultData)
                          {
                              bool success;
                              (success, resultData) = target.call(callData);
                              if (!success) {
                                  resultData.rrevert();
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./interfaces/IAuthorizableV06.sol";
                      import "./errors/LibRichErrorsV06.sol";
                      import "./errors/LibAuthorizableRichErrorsV06.sol";
                      import "./OwnableV06.sol";
                      // solhint-disable no-empty-blocks
                      contract AuthorizableV06 is
                          OwnableV06,
                          IAuthorizableV06
                      {
                          /// @dev Only authorized addresses can invoke functions with this modifier.
                          modifier onlyAuthorized {
                              _assertSenderIsAuthorized();
                              _;
                          }
                          // @dev Whether an address is authorized to call privileged functions.
                          // @param 0 Address to query.
                          // @return 0 Whether the address is authorized.
                          mapping (address => bool) public override authorized;
                          // @dev Whether an address is authorized to call privileged functions.
                          // @param 0 Index of authorized address.
                          // @return 0 Authorized address.
                          address[] public override authorities;
                          /// @dev Initializes the `owner` address.
                          constructor()
                              public
                              OwnableV06()
                          {}
                          /// @dev Authorizes an address.
                          /// @param target Address to authorize.
                          function addAuthorizedAddress(address target)
                              external
                              override
                              onlyOwner
                          {
                              _addAuthorizedAddress(target);
                          }
                          /// @dev Removes authorizion of an address.
                          /// @param target Address to remove authorization from.
                          function removeAuthorizedAddress(address target)
                              external
                              override
                              onlyOwner
                          {
                              if (!authorized[target]) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.TargetNotAuthorizedError(target));
                              }
                              for (uint256 i = 0; i < authorities.length; i++) {
                                  if (authorities[i] == target) {
                                      _removeAuthorizedAddressAtIndex(target, i);
                                      break;
                                  }
                              }
                          }
                          /// @dev Removes authorizion of an address.
                          /// @param target Address to remove authorization from.
                          /// @param index Index of target in authorities array.
                          function removeAuthorizedAddressAtIndex(
                              address target,
                              uint256 index
                          )
                              external
                              override
                              onlyOwner
                          {
                              _removeAuthorizedAddressAtIndex(target, index);
                          }
                          /// @dev Gets all authorized addresses.
                          /// @return Array of authorized addresses.
                          function getAuthorizedAddresses()
                              external
                              override
                              view
                              returns (address[] memory)
                          {
                              return authorities;
                          }
                          /// @dev Reverts if msg.sender is not authorized.
                          function _assertSenderIsAuthorized()
                              internal
                              view
                          {
                              if (!authorized[msg.sender]) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.SenderNotAuthorizedError(msg.sender));
                              }
                          }
                          /// @dev Authorizes an address.
                          /// @param target Address to authorize.
                          function _addAuthorizedAddress(address target)
                              internal
                          {
                              // Ensure that the target is not the zero address.
                              if (target == address(0)) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.ZeroCantBeAuthorizedError());
                              }
                              // Ensure that the target is not already authorized.
                              if (authorized[target]) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.TargetAlreadyAuthorizedError(target));
                              }
                              authorized[target] = true;
                              authorities.push(target);
                              emit AuthorizedAddressAdded(target, msg.sender);
                          }
                          /// @dev Removes authorizion of an address.
                          /// @param target Address to remove authorization from.
                          /// @param index Index of target in authorities array.
                          function _removeAuthorizedAddressAtIndex(
                              address target,
                              uint256 index
                          )
                              internal
                          {
                              if (!authorized[target]) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.TargetNotAuthorizedError(target));
                              }
                              if (index >= authorities.length) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.IndexOutOfBoundsError(
                                      index,
                                      authorities.length
                                  ));
                              }
                              if (authorities[index] != target) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.AuthorizedAddressMismatchError(
                                      authorities[index],
                                      target
                                  ));
                              }
                              delete authorized[target];
                              authorities[index] = authorities[authorities.length - 1];
                              authorities.pop();
                              emit AuthorizedAddressRemoved(target, msg.sender);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./IOwnableV06.sol";
                      interface IAuthorizableV06 is
                          IOwnableV06
                      {
                          // Event logged when a new address is authorized.
                          event AuthorizedAddressAdded(
                              address indexed target,
                              address indexed caller
                          );
                          // Event logged when a currently authorized address is unauthorized.
                          event AuthorizedAddressRemoved(
                              address indexed target,
                              address indexed caller
                          );
                          /// @dev Authorizes an address.
                          /// @param target Address to authorize.
                          function addAuthorizedAddress(address target)
                              external;
                          /// @dev Removes authorizion of an address.
                          /// @param target Address to remove authorization from.
                          function removeAuthorizedAddress(address target)
                              external;
                          /// @dev Removes authorizion of an address.
                          /// @param target Address to remove authorization from.
                          /// @param index Index of target in authorities array.
                          function removeAuthorizedAddressAtIndex(
                              address target,
                              uint256 index
                          )
                              external;
                          /// @dev Gets all authorized addresses.
                          /// @return authorizedAddresses Array of authorized addresses.
                          function getAuthorizedAddresses()
                              external
                              view
                              returns (address[] memory authorizedAddresses);
                          /// @dev Whether an adderss is authorized to call privileged functions.
                          /// @param addr Address to query.
                          /// @return isAuthorized Whether the address is authorized.
                          function authorized(address addr) external view returns (bool isAuthorized);
                          /// @dev All addresseses authorized to call privileged functions.
                          /// @param idx Index of authorized address.
                          /// @return addr Authorized address.
                          function authorities(uint256 idx) external view returns (address addr);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      interface IOwnableV06 {
                          /// @dev Emitted by Ownable when ownership is transferred.
                          /// @param previousOwner The previous owner of the contract.
                          /// @param newOwner The new owner of the contract.
                          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                          /// @dev Transfers ownership of the contract to a new address.
                          /// @param newOwner The address that will become the owner.
                          function transferOwnership(address newOwner) external;
                          /// @dev The owner of this contract.
                          /// @return ownerAddress The owner address.
                          function owner() external view returns (address ownerAddress);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibAuthorizableRichErrorsV06 {
                          // bytes4(keccak256("AuthorizedAddressMismatchError(address,address)"))
                          bytes4 internal constant AUTHORIZED_ADDRESS_MISMATCH_ERROR_SELECTOR =
                              0x140a84db;
                          // bytes4(keccak256("IndexOutOfBoundsError(uint256,uint256)"))
                          bytes4 internal constant INDEX_OUT_OF_BOUNDS_ERROR_SELECTOR =
                              0xe9f83771;
                          // bytes4(keccak256("SenderNotAuthorizedError(address)"))
                          bytes4 internal constant SENDER_NOT_AUTHORIZED_ERROR_SELECTOR =
                              0xb65a25b9;
                          // bytes4(keccak256("TargetAlreadyAuthorizedError(address)"))
                          bytes4 internal constant TARGET_ALREADY_AUTHORIZED_ERROR_SELECTOR =
                              0xde16f1a0;
                          // bytes4(keccak256("TargetNotAuthorizedError(address)"))
                          bytes4 internal constant TARGET_NOT_AUTHORIZED_ERROR_SELECTOR =
                              0xeb5108a2;
                          // bytes4(keccak256("ZeroCantBeAuthorizedError()"))
                          bytes internal constant ZERO_CANT_BE_AUTHORIZED_ERROR_BYTES =
                              hex"57654fe4";
                          // solhint-disable func-name-mixedcase
                          function AuthorizedAddressMismatchError(
                              address authorized,
                              address target
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  AUTHORIZED_ADDRESS_MISMATCH_ERROR_SELECTOR,
                                  authorized,
                                  target
                              );
                          }
                          function IndexOutOfBoundsError(
                              uint256 index,
                              uint256 length
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  INDEX_OUT_OF_BOUNDS_ERROR_SELECTOR,
                                  index,
                                  length
                              );
                          }
                          function SenderNotAuthorizedError(address sender)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  SENDER_NOT_AUTHORIZED_ERROR_SELECTOR,
                                  sender
                              );
                          }
                          function TargetAlreadyAuthorizedError(address target)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  TARGET_ALREADY_AUTHORIZED_ERROR_SELECTOR,
                                  target
                              );
                          }
                          function TargetNotAuthorizedError(address target)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  TARGET_NOT_AUTHORIZED_ERROR_SELECTOR,
                                  target
                              );
                          }
                          function ZeroCantBeAuthorizedError()
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return ZERO_CANT_BE_AUTHORIZED_ERROR_BYTES;
                          }
                      }
                      /*
                        Copyright 2019 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./interfaces/IOwnableV06.sol";
                      import "./errors/LibRichErrorsV06.sol";
                      import "./errors/LibOwnableRichErrorsV06.sol";
                      contract OwnableV06 is
                          IOwnableV06
                      {
                          /// @dev The owner of this contract.
                          /// @return 0 The owner address.
                          address public override owner;
                          constructor() public {
                              owner = msg.sender;
                          }
                          modifier onlyOwner() {
                              _assertSenderIsOwner();
                              _;
                          }
                          /// @dev Change the owner of this contract.
                          /// @param newOwner New owner address.
                          function transferOwnership(address newOwner)
                              public
                              override
                              onlyOwner
                          {
                              if (newOwner == address(0)) {
                                  LibRichErrorsV06.rrevert(LibOwnableRichErrorsV06.TransferOwnerToZeroError());
                              } else {
                                  owner = newOwner;
                                  emit OwnershipTransferred(msg.sender, newOwner);
                              }
                          }
                          function _assertSenderIsOwner()
                              internal
                              view
                          {
                              if (msg.sender != owner) {
                                  LibRichErrorsV06.rrevert(LibOwnableRichErrorsV06.OnlyOwnerError(
                                      msg.sender,
                                      owner
                                  ));
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibOwnableRichErrorsV06 {
                          // bytes4(keccak256("OnlyOwnerError(address,address)"))
                          bytes4 internal constant ONLY_OWNER_ERROR_SELECTOR =
                              0x1de45ad1;
                          // bytes4(keccak256("TransferOwnerToZeroError()"))
                          bytes internal constant TRANSFER_OWNER_TO_ZERO_ERROR_BYTES =
                              hex"e69edc3e";
                          // solhint-disable func-name-mixedcase
                          function OnlyOwnerError(
                              address sender,
                              address owner
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  ONLY_OWNER_ERROR_SELECTOR,
                                  sender,
                                  owner
                              );
                          }
                          function TransferOwnerToZeroError()
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return TRANSFER_OWNER_TO_ZERO_ERROR_BYTES;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/interfaces/IAuthorizableV06.sol";
                      /// @dev The allowance target for the TokenSpender feature.
                      interface IAllowanceTarget is
                          IAuthorizableV06
                      {
                          /// @dev Execute an arbitrary call. Only an authority can call this.
                          /// @param target The call target.
                          /// @param callData The call data.
                          /// @return resultData The data returned by the call.
                          function executeCall(
                              address payable target,
                              bytes calldata callData
                          )
                              external
                              returns (bytes memory resultData);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibOwnableRichErrorsV06.sol";
                      import "../errors/LibWalletRichErrors.sol";
                      import "./IFlashWallet.sol";
                      /// @dev A contract that can execute arbitrary calls from its owner.
                      contract FlashWallet is
                          IFlashWallet
                      {
                          // solhint-disable no-unused-vars,indent,no-empty-blocks
                          using LibRichErrorsV06 for bytes;
                          // solhint-disable
                          /// @dev Store the owner/deployer as an immutable to make this contract stateless.
                          address public override immutable owner;
                          // solhint-enable
                          constructor() public {
                              // The deployer is the owner.
                              owner = msg.sender;
                          }
                          /// @dev Allows only the (immutable) owner to call a function.
                          modifier onlyOwner() virtual {
                              if (msg.sender != owner) {
                                  LibOwnableRichErrorsV06.OnlyOwnerError(
                                      msg.sender,
                                      owner
                                  ).rrevert();
                              }
                              _;
                          }
                          /// @dev Execute an arbitrary call. Only an authority can call this.
                          /// @param target The call target.
                          /// @param callData The call data.
                          /// @param value Ether to attach to the call.
                          /// @return resultData The data returned by the call.
                          function executeCall(
                              address payable target,
                              bytes calldata callData,
                              uint256 value
                          )
                              external
                              payable
                              override
                              onlyOwner
                              returns (bytes memory resultData)
                          {
                              bool success;
                              (success, resultData) = target.call{value: value}(callData);
                              if (!success) {
                                  LibWalletRichErrors
                                      .WalletExecuteCallFailedError(
                                          address(this),
                                          target,
                                          callData,
                                          value,
                                          resultData
                                      )
                                      .rrevert();
                              }
                          }
                          /// @dev Execute an arbitrary delegatecall, in the context of this puppet.
                          ///      Only an authority can call this.
                          /// @param target The call target.
                          /// @param callData The call data.
                          /// @return resultData The data returned by the call.
                          function executeDelegateCall(
                              address payable target,
                              bytes calldata callData
                          )
                              external
                              payable
                              override
                              onlyOwner
                              returns (bytes memory resultData)
                          {
                              bool success;
                              (success, resultData) = target.delegatecall(callData);
                              if (!success) {
                                  LibWalletRichErrors
                                      .WalletExecuteDelegateCallFailedError(
                                          address(this),
                                          target,
                                          callData,
                                          resultData
                                      )
                                      .rrevert();
                              }
                          }
                          // solhint-disable
                          /// @dev Allows this contract to receive ether.
                          receive() external override payable {}
                          // solhint-enable
                          /// @dev Signal support for receiving ERC1155 tokens.
                          /// @param interfaceID The interface ID, as per ERC-165 rules.
                          /// @return hasSupport `true` if this contract supports an ERC-165 interface.
                          function supportsInterface(bytes4 interfaceID)
                              external
                              pure
                              returns (bool hasSupport)
                          {
                              return  interfaceID == this.supportsInterface.selector ||
                                      interfaceID == this.onERC1155Received.selector ^ this.onERC1155BatchReceived.selector ||
                                      interfaceID == this.tokenFallback.selector;
                          }
                          ///  @dev Allow this contract to receive ERC1155 tokens.
                          ///  @return success  `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
                          function onERC1155Received(
                              address, // operator,
                              address, // from,
                              uint256, // id,
                              uint256, // value,
                              bytes calldata //data
                          )
                              external
                              pure
                              returns (bytes4 success)
                          {
                              return this.onERC1155Received.selector;
                          }
                          ///  @dev Allow this contract to receive ERC1155 tokens.
                          ///  @return success  `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
                          function onERC1155BatchReceived(
                              address, // operator,
                              address, // from,
                              uint256[] calldata, // ids,
                              uint256[] calldata, // values,
                              bytes calldata // data
                          )
                              external
                              pure
                              returns (bytes4 success)
                          {
                              return this.onERC1155BatchReceived.selector;
                          }
                          /// @dev Allows this contract to receive ERC223 tokens.
                          function tokenFallback(
                              address, // from,
                              uint256, // value,
                              bytes calldata // value
                          )
                              external
                              pure
                          {}
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/interfaces/IOwnableV06.sol";
                      /// @dev A contract that can execute arbitrary calls from its owner.
                      interface IFlashWallet {
                          /// @dev Execute an arbitrary call. Only an authority can call this.
                          /// @param target The call target.
                          /// @param callData The call data.
                          /// @param value Ether to attach to the call.
                          /// @return resultData The data returned by the call.
                          function executeCall(
                              address payable target,
                              bytes calldata callData,
                              uint256 value
                          )
                              external
                              payable
                              returns (bytes memory resultData);
                          /// @dev Execute an arbitrary delegatecall, in the context of this puppet.
                          ///      Only an authority can call this.
                          /// @param target The call target.
                          /// @param callData The call data.
                          /// @return resultData The data returned by the call.
                          function executeDelegateCall(
                              address payable target,
                              bytes calldata callData
                          )
                              external
                              payable
                              returns (bytes memory resultData);
                          /// @dev Allows the puppet to receive ETH.
                          receive() external payable;
                          /// @dev Fetch the immutable owner/deployer of this contract.
                          /// @return owner_ The immutable owner/deployer/
                          function owner() external view returns (address owner_);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/AuthorizableV06.sol";
                      /// @dev A contract with a `die()` function.
                      interface IKillable {
                          function die() external;
                      }
                      /// @dev Deployer contract for ERC20 transformers.
                      ///      Only authorities may call `deploy()` and `kill()`.
                      contract TransformerDeployer is
                          AuthorizableV06
                      {
                          /// @dev Emitted when a contract is deployed via `deploy()`.
                          /// @param deployedAddress The address of the deployed contract.
                          /// @param nonce The deployment nonce.
                          /// @param sender The caller of `deploy()`.
                          event Deployed(address deployedAddress, uint256 nonce, address sender);
                          /// @dev Emitted when a contract is killed via `kill()`.
                          /// @param target The address of the contract being killed..
                          /// @param sender The caller of `kill()`.
                          event Killed(address target, address sender);
                          // @dev The current nonce of this contract.
                          uint256 public nonce = 1;
                          // @dev Mapping of deployed contract address to deployment nonce.
                          mapping (address => uint256) public toDeploymentNonce;
                          /// @dev Create this contract and register authorities.
                          constructor(address[] memory authorities) public {
                              for (uint256 i = 0; i < authorities.length; ++i) {
                                  _addAuthorizedAddress(authorities[i]);
                              }
                          }
                          /// @dev Deploy a new contract. Only callable by an authority.
                          ///      Any attached ETH will also be forwarded.
                          function deploy(bytes memory bytecode)
                              public
                              payable
                              onlyAuthorized
                              returns (address deployedAddress)
                          {
                              uint256 deploymentNonce = nonce;
                              nonce += 1;
                              assembly {
                                  deployedAddress := create(callvalue(), add(bytecode, 32), mload(bytecode))
                              }
                              toDeploymentNonce[deployedAddress] = deploymentNonce;
                              emit Deployed(deployedAddress, deploymentNonce, msg.sender);
                          }
                          /// @dev Call `die()` on a contract. Only callable by an authority.
                          function kill(IKillable target)
                              public
                              onlyAuthorized
                          {
                              target.die();
                              emit Killed(address(target), msg.sender);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      /// @dev Basic interface for a feature contract.
                      interface IFeature {
                          // solhint-disable func-name-mixedcase
                          /// @dev The name of this feature set.
                          function FEATURE_NAME() external view returns (string memory name);
                          /// @dev The version of this feature set.
                          function FEATURE_VERSION() external view returns (uint256 version);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      /// @dev Meta-transactions feature.
                      interface IMetaTransactions {
                          /// @dev Describes an exchange proxy meta transaction.
                          struct MetaTransactionData {
                              // Signer of meta-transaction. On whose behalf to execute the MTX.
                              address signer;
                              // Required sender, or NULL for anyone.
                              address sender;
                              // Minimum gas price.
                              uint256 minGasPrice;
                              // Maximum gas price.
                              uint256 maxGasPrice;
                              // MTX is invalid after this time.
                              uint256 expirationTime;
                              // Nonce to make this MTX unique.
                              uint256 salt;
                              // Encoded call data to a function on the exchange proxy.
                              bytes callData;
                              // Amount of ETH to attach to the call.
                              uint256 value;
                              // ERC20 fee `signer` pays `sender`.
                              IERC20TokenV06 feeToken;
                              // ERC20 fee amount.
                              uint256 feeAmount;
                          }
                          /// @dev Emitted whenever a meta-transaction is executed via
                          ///      `executeMetaTransaction()` or `executeMetaTransactions()`.
                          /// @param hash The meta-transaction hash.
                          /// @param selector The selector of the function being executed.
                          /// @param signer Who to execute the meta-transaction on behalf of.
                          /// @param sender Who executed the meta-transaction.
                          event MetaTransactionExecuted(
                              bytes32 hash,
                              bytes4 indexed selector,
                              address signer,
                              address sender
                          );
                          /// @dev Execute a single meta-transaction.
                          /// @param mtx The meta-transaction.
                          /// @param signature The signature by `mtx.signer`.
                          /// @return returnData The ABI-encoded result of the underlying call.
                          function executeMetaTransaction(
                              MetaTransactionData calldata mtx,
                              bytes calldata signature
                          )
                              external
                              payable
                              returns (bytes memory returnData);
                          /// @dev Execute multiple meta-transactions.
                          /// @param mtxs The meta-transactions.
                          /// @param signatures The signature by each respective `mtx.signer`.
                          /// @return returnDatas The ABI-encoded results of the underlying calls.
                          function executeMetaTransactions(
                              MetaTransactionData[] calldata mtxs,
                              bytes[] calldata signatures
                          )
                              external
                              payable
                              returns (bytes[] memory returnDatas);
                          /// @dev Execute a meta-transaction via `sender`. Privileged variant.
                          ///      Only callable from within.
                          /// @param sender Who is executing the meta-transaction..
                          /// @param mtx The meta-transaction.
                          /// @param signature The signature by `mtx.signer`.
                          /// @return returnData The ABI-encoded result of the underlying call.
                          function _executeMetaTransaction(
                              address sender,
                              MetaTransactionData calldata mtx,
                              bytes calldata signature
                          )
                              external
                              payable
                              returns (bytes memory returnData);
                          /// @dev Get the block at which a meta-transaction has been executed.
                          /// @param mtx The meta-transaction.
                          /// @return blockNumber The block height when the meta-transactioin was executed.
                          function getMetaTransactionExecutedBlock(MetaTransactionData calldata mtx)
                              external
                              view
                              returns (uint256 blockNumber);
                          /// @dev Get the block at which a meta-transaction hash has been executed.
                          /// @param mtxHash The meta-transaction hash.
                          /// @return blockNumber The block height when the meta-transactioin was executed.
                          function getMetaTransactionHashExecutedBlock(bytes32 mtxHash)
                              external
                              view
                              returns (uint256 blockNumber);
                          /// @dev Get the EIP712 hash of a meta-transaction.
                          /// @param mtx The meta-transaction.
                          /// @return mtxHash The EIP712 hash of `mtx`.
                          function getMetaTransactionHash(MetaTransactionData calldata mtx)
                              external
                              view
                              returns (bytes32 mtxHash);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      interface IERC20TokenV06 {
                          // solhint-disable no-simple-event-func-name
                          event Transfer(
                              address indexed from,
                              address indexed to,
                              uint256 value
                          );
                          event Approval(
                              address indexed owner,
                              address indexed spender,
                              uint256 value
                          );
                          /// @dev send `value` token to `to` from `msg.sender`
                          /// @param to The address of the recipient
                          /// @param value The amount of token to be transferred
                          /// @return True if transfer was successful
                          function transfer(address to, uint256 value)
                              external
                              returns (bool);
                          /// @dev send `value` token to `to` from `from` on the condition it is approved by `from`
                          /// @param from The address of the sender
                          /// @param to The address of the recipient
                          /// @param value The amount of token to be transferred
                          /// @return True if transfer was successful
                          function transferFrom(
                              address from,
                              address to,
                              uint256 value
                          )
                              external
                              returns (bool);
                          /// @dev `msg.sender` approves `spender` to spend `value` tokens
                          /// @param spender The address of the account able to transfer the tokens
                          /// @param value The amount of wei to be approved for transfer
                          /// @return Always true if the call has enough gas to complete execution
                          function approve(address spender, uint256 value)
                              external
                              returns (bool);
                          /// @dev Query total supply of token
                          /// @return Total supply of token
                          function totalSupply()
                              external
                              view
                              returns (uint256);
                          /// @dev Get the balance of `owner`.
                          /// @param owner The address from which the balance will be retrieved
                          /// @return Balance of owner
                          function balanceOf(address owner)
                              external
                              view
                              returns (uint256);
                          /// @dev Get the allowance for `spender` to spend from `owner`.
                          /// @param owner The address of the account owning tokens
                          /// @param spender The address of the account able to transfer the tokens
                          /// @return Amount of remaining tokens allowed to spent
                          function allowance(address owner, address spender)
                              external
                              view
                              returns (uint256);
                          /// @dev Get the number of decimals this token has.
                          function decimals()
                              external
                              view
                              returns (uint8);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/interfaces/IOwnableV06.sol";
                      // solhint-disable no-empty-blocks
                      /// @dev Owner management and migration features.
                      interface IOwnable is
                          IOwnableV06
                      {
                          /// @dev Emitted when `migrate()` is called.
                          /// @param caller The caller of `migrate()`.
                          /// @param migrator The migration contract.
                          /// @param newOwner The address of the new owner.
                          event Migrated(address caller, address migrator, address newOwner);
                          /// @dev Execute a migration function in the context of the ZeroEx contract.
                          ///      The result of the function being called should be the magic bytes
                          ///      0x2c64c5ef (`keccack('MIGRATE_SUCCESS')`). Only callable by the owner.
                          ///      The owner will be temporarily set to `address(this)` inside the call.
                          ///      Before returning, the owner will be set to `newOwner`.
                          /// @param target The migrator contract address.
                          /// @param newOwner The address of the new owner.
                          /// @param data The call data.
                          function migrate(address target, bytes calldata data, address newOwner) external;
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      /// @dev Feature for validating signatures.
                      interface ISignatureValidator {
                         /// @dev Allowed signature types.
                          enum SignatureType {
                              Illegal,                     // 0x00, default value
                              Invalid,                     // 0x01
                              EIP712,                      // 0x02
                              EthSign,                     // 0x03
                              NSignatureTypes              // 0x04, number of signature types. Always leave at end.
                          }
                          /// @dev Validate that `hash` was signed by `signer` given `signature`.
                          ///      Reverts otherwise.
                          /// @param hash The hash that was signed.
                          /// @param signer The signer of the hash.
                          /// @param signature The signature. The last byte of this signature should
                          ///        be a member of the `SignatureType` enum.
                          function validateHashSignature(
                              bytes32 hash,
                              address signer,
                              bytes calldata signature
                          )
                              external
                              view;
                          /// @dev Check that `hash` was signed by `signer` given `signature`.
                          /// @param hash The hash that was signed.
                          /// @param signer The signer of the hash.
                          /// @param signature The signature. The last byte of this signature should
                          ///        be a member of the `SignatureType` enum.
                          /// @return isValid `true` on success.
                          function isValidHashSignature(
                              bytes32 hash,
                              address signer,
                              bytes calldata signature
                          )
                              external
                              view
                              returns (bool isValid);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      /// @dev Basic registry management features.
                      interface ISimpleFunctionRegistry {
                          /// @dev A function implementation was updated via `extend()` or `rollback()`.
                          /// @param selector The function selector.
                          /// @param oldImpl The implementation contract address being replaced.
                          /// @param newImpl The replacement implementation contract address.
                          event ProxyFunctionUpdated(bytes4 indexed selector, address oldImpl, address newImpl);
                          /// @dev Roll back to a prior implementation of a function.
                          /// @param selector The function selector.
                          /// @param targetImpl The address of an older implementation of the function.
                          function rollback(bytes4 selector, address targetImpl) external;
                          /// @dev Register or replace a function.
                          /// @param selector The function selector.
                          /// @param impl The implementation contract for the function.
                          function extend(bytes4 selector, address impl) external;
                          /// @dev Retrieve the length of the rollback history for a function.
                          /// @param selector The function selector.
                          /// @return rollbackLength The number of items in the rollback history for
                          ///         the function.
                          function getRollbackLength(bytes4 selector)
                              external
                              view
                              returns (uint256 rollbackLength);
                          /// @dev Retrieve an entry in the rollback history for a function.
                          /// @param selector The function selector.
                          /// @param idx The index in the rollback history.
                          /// @return impl An implementation address for the function at
                          ///         index `idx`.
                          function getRollbackEntryAtIndex(bytes4 selector, uint256 idx)
                              external
                              view
                              returns (address impl);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      /// @dev Feature that allows spending token allowances.
                      interface ITokenSpender {
                          /// @dev Transfers ERC20 tokens from `owner` to `to`.
                          ///      Only callable from within.
                          /// @param token The token to spend.
                          /// @param owner The owner of the tokens.
                          /// @param to The recipient of the tokens.
                          /// @param amount The amount of `token` to transfer.
                          function _spendERC20Tokens(
                              IERC20TokenV06 token,
                              address owner,
                              address to,
                              uint256 amount
                          )
                              external;
                          /// @dev Gets the maximum amount of an ERC20 token `token` that can be
                          ///      pulled from `owner`.
                          /// @param token The token to spend.
                          /// @param owner The owner of the tokens.
                          /// @return amount The amount of tokens that can be pulled.
                          function getSpendableERC20BalanceOf(IERC20TokenV06 token, address owner)
                              external
                              view
                              returns (uint256 amount);
                          /// @dev Get the address of the allowance target.
                          /// @return target The target of token allowances.
                          function getAllowanceTarget() external view returns (address target);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "../transformers/IERC20Transformer.sol";
                      import "../external/IFlashWallet.sol";
                      /// @dev Feature to composably transform between ERC20 tokens.
                      interface ITransformERC20 {
                          /// @dev Defines a transformation to run in `transformERC20()`.
                          struct Transformation {
                              // The deployment nonce for the transformer.
                              // The address of the transformer contract will be derived from this
                              // value.
                              uint32 deploymentNonce;
                              // Arbitrary data to pass to the transformer.
                              bytes data;
                          }
                          /// @dev Raised upon a successful `transformERC20`.
                          /// @param taker The taker (caller) address.
                          /// @param inputToken The token being provided by the taker.
                          ///        If `0xeee...`, ETH is implied and should be provided with the call.`
                          /// @param outputToken The token to be acquired by the taker.
                          ///        `0xeee...` implies ETH.
                          /// @param inputTokenAmount The amount of `inputToken` to take from the taker.
                          /// @param outputTokenAmount The amount of `outputToken` received by the taker.
                          event TransformedERC20(
                              address indexed taker,
                              address inputToken,
                              address outputToken,
                              uint256 inputTokenAmount,
                              uint256 outputTokenAmount
                          );
                          /// @dev Raised when `setTransformerDeployer()` is called.
                          /// @param transformerDeployer The new deployer address.
                          event TransformerDeployerUpdated(address transformerDeployer);
                          /// @dev Replace the allowed deployer for transformers.
                          ///      Only callable by the owner.
                          /// @param transformerDeployer The address of the trusted deployer for transformers.
                          function setTransformerDeployer(address transformerDeployer)
                              external;
                          /// @dev Deploy a new flash wallet instance and replace the current one with it.
                          ///      Useful if we somehow break the current wallet instance.
                          ///       Only callable by the owner.
                          /// @return wallet The new wallet instance.
                          function createTransformWallet()
                              external
                              returns (IFlashWallet wallet);
                          /// @dev Executes a series of transformations to convert an ERC20 `inputToken`
                          ///      to an ERC20 `outputToken`.
                          /// @param inputToken The token being provided by the sender.
                          ///        If `0xeee...`, ETH is implied and should be provided with the call.`
                          /// @param outputToken The token to be acquired by the sender.
                          ///        `0xeee...` implies ETH.
                          /// @param inputTokenAmount The amount of `inputToken` to take from the sender.
                          /// @param minOutputTokenAmount The minimum amount of `outputToken` the sender
                          ///        must receive for the entire transformation to succeed.
                          /// @param transformations The transformations to execute on the token balance(s)
                          ///        in sequence.
                          /// @return outputTokenAmount The amount of `outputToken` received by the sender.
                          function transformERC20(
                              IERC20TokenV06 inputToken,
                              IERC20TokenV06 outputToken,
                              uint256 inputTokenAmount,
                              uint256 minOutputTokenAmount,
                              Transformation[] calldata transformations
                          )
                              external
                              payable
                              returns (uint256 outputTokenAmount);
                          /// @dev Internal version of `transformERC20()`. Only callable from within.
                          /// @param callDataHash Hash of the ingress calldata.
                          /// @param taker The taker address.
                          /// @param inputToken The token being provided by the taker.
                          ///        If `0xeee...`, ETH is implied and should be provided with the call.`
                          /// @param outputToken The token to be acquired by the taker.
                          ///        `0xeee...` implies ETH.
                          /// @param inputTokenAmount The amount of `inputToken` to take from the taker.
                          /// @param minOutputTokenAmount The minimum amount of `outputToken` the taker
                          ///        must receive for the entire transformation to succeed.
                          /// @param transformations The transformations to execute on the token balance(s)
                          ///        in sequence.
                          /// @return outputTokenAmount The amount of `outputToken` received by the taker.
                          function _transformERC20(
                              bytes32 callDataHash,
                              address payable taker,
                              IERC20TokenV06 inputToken,
                              IERC20TokenV06 outputToken,
                              uint256 inputTokenAmount,
                              uint256 minOutputTokenAmount,
                              Transformation[] calldata transformations
                          )
                              external
                              payable
                              returns (uint256 outputTokenAmount);
                          /// @dev Return the current wallet instance that will serve as the execution
                          ///      context for transformations.
                          /// @return wallet The wallet instance.
                          function getTransformWallet()
                              external
                              view
                              returns (IFlashWallet wallet);
                          /// @dev Return the allowed deployer for transformers.
                          /// @return deployer The transform deployer address.
                          function getTransformerDeployer()
                              external
                              view
                              returns (address deployer);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      /// @dev A transformation callback used in `TransformERC20.transformERC20()`.
                      interface IERC20Transformer {
                          /// @dev Called from `TransformERC20.transformERC20()`. This will be
                          ///      delegatecalled in the context of the FlashWallet instance being used.
                          /// @param callDataHash The hash of the `TransformERC20.transformERC20()` calldata.
                          /// @param taker The taker address (caller of `TransformERC20.transformERC20()`).
                          /// @param data Arbitrary data to pass to the transformer.
                          /// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
                          function transform(
                              bytes32 callDataHash,
                              address payable taker,
                              bytes calldata data
                          )
                              external
                              returns (bytes4 success);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
                      import "../errors/LibMetaTransactionsRichErrors.sol";
                      import "../fixins/FixinCommon.sol";
                      import "../fixins/FixinEIP712.sol";
                      import "../migrations/LibMigrate.sol";
                      import "../storage/LibMetaTransactionsStorage.sol";
                      import "./IMetaTransactions.sol";
                      import "./ITransformERC20.sol";
                      import "./ISignatureValidator.sol";
                      import "./ITokenSpender.sol";
                      import "./IFeature.sol";
                      /// @dev MetaTransactions feature.
                      contract MetaTransactions is
                          IFeature,
                          IMetaTransactions,
                          FixinCommon,
                          FixinEIP712
                      {
                          using LibBytesV06 for bytes;
                          using LibRichErrorsV06 for bytes;
                          /// @dev Intermediate state vars to avoid stack overflows.
                          struct ExecuteState {
                              address sender;
                              bytes32 hash;
                              MetaTransactionData mtx;
                              bytes signature;
                              bytes4 selector;
                              uint256 selfBalance;
                              uint256 executedBlockNumber;
                          }
                          struct TransformERC20Args {
                              IERC20TokenV06 inputToken;
                              IERC20TokenV06 outputToken;
                              uint256 inputTokenAmount;
                              uint256 minOutputTokenAmount;
                              ITransformERC20.Transformation[] transformations;
                          }
                          /// @dev Name of this feature.
                          string public constant override FEATURE_NAME = "MetaTransactions";
                          /// @dev Version of this feature.
                          uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
                          /// @dev EIP712 typehash of the `MetaTransactionData` struct.
                          bytes32 public immutable MTX_EIP712_TYPEHASH = keccak256(
                              "MetaTransactionData("
                                  "address signer,"
                                  "address sender,"
                                  "uint256 minGasPrice,"
                                  "uint256 maxGasPrice,"
                                  "uint256 expirationTime,"
                                  "uint256 salt,"
                                  "bytes callData,"
                                  "uint256 value,"
                                  "address feeToken,"
                                  "uint256 feeAmount"
                              ")"
                          );
                          constructor(address zeroExAddress)
                              public
                              FixinCommon()
                              FixinEIP712(zeroExAddress)
                          {
                              // solhint-disable-next-line no-empty-blocks
                          }
                          /// @dev Initialize and register this feature.
                          ///      Should be delegatecalled by `Migrate.migrate()`.
                          /// @return success `LibMigrate.SUCCESS` on success.
                          function migrate()
                              external
                              returns (bytes4 success)
                          {
                              _registerFeatureFunction(this.executeMetaTransaction.selector);
                              _registerFeatureFunction(this.executeMetaTransactions.selector);
                              _registerFeatureFunction(this._executeMetaTransaction.selector);
                              _registerFeatureFunction(this.getMetaTransactionExecutedBlock.selector);
                              _registerFeatureFunction(this.getMetaTransactionHashExecutedBlock.selector);
                              _registerFeatureFunction(this.getMetaTransactionHash.selector);
                              return LibMigrate.MIGRATE_SUCCESS;
                          }
                          /// @dev Execute a single meta-transaction.
                          /// @param mtx The meta-transaction.
                          /// @param signature The signature by `mtx.signer`.
                          /// @return returnData The ABI-encoded result of the underlying call.
                          function executeMetaTransaction(
                              MetaTransactionData memory mtx,
                              bytes memory signature
                          )
                              public
                              payable
                              override
                              returns (bytes memory returnData)
                          {
                              return _executeMetaTransactionPrivate(
                                  msg.sender,
                                  mtx,
                                  signature
                              );
                          }
                          /// @dev Execute multiple meta-transactions.
                          /// @param mtxs The meta-transactions.
                          /// @param signatures The signature by each respective `mtx.signer`.
                          /// @return returnDatas The ABI-encoded results of the underlying calls.
                          function executeMetaTransactions(
                              MetaTransactionData[] memory mtxs,
                              bytes[] memory signatures
                          )
                              public
                              payable
                              override
                              returns (bytes[] memory returnDatas)
                          {
                              if (mtxs.length != signatures.length) {
                                  LibMetaTransactionsRichErrors.InvalidMetaTransactionsArrayLengthsError(
                                      mtxs.length,
                                      signatures.length
                                  ).rrevert();
                              }
                              returnDatas = new bytes[](mtxs.length);
                              for (uint256 i = 0; i < mtxs.length; ++i) {
                                  returnDatas[i] = _executeMetaTransactionPrivate(
                                      msg.sender,
                                      mtxs[i],
                                      signatures[i]
                                  );
                              }
                          }
                          /// @dev Execute a meta-transaction via `sender`. Privileged variant.
                          ///      Only callable from within.
                          /// @param sender Who is executing the meta-transaction..
                          /// @param mtx The meta-transaction.
                          /// @param signature The signature by `mtx.signer`.
                          /// @return returnData The ABI-encoded result of the underlying call.
                          function _executeMetaTransaction(
                              address sender,
                              MetaTransactionData memory mtx,
                              bytes memory signature
                          )
                              public
                              payable
                              override
                              onlySelf
                              returns (bytes memory returnData)
                          {
                              return _executeMetaTransactionPrivate(sender, mtx, signature);
                          }
                          /// @dev Get the block at which a meta-transaction has been executed.
                          /// @param mtx The meta-transaction.
                          /// @return blockNumber The block height when the meta-transactioin was executed.
                          function getMetaTransactionExecutedBlock(MetaTransactionData memory mtx)
                              public
                              override
                              view
                              returns (uint256 blockNumber)
                          {
                              return getMetaTransactionHashExecutedBlock(getMetaTransactionHash(mtx));
                          }
                          /// @dev Get the block at which a meta-transaction hash has been executed.
                          /// @param mtxHash The meta-transaction hash.
                          /// @return blockNumber The block height when the meta-transactioin was executed.
                          function getMetaTransactionHashExecutedBlock(bytes32 mtxHash)
                              public
                              override
                              view
                              returns (uint256 blockNumber)
                          {
                              return LibMetaTransactionsStorage.getStorage().mtxHashToExecutedBlockNumber[mtxHash];
                          }
                          /// @dev Get the EIP712 hash of a meta-transaction.
                          /// @param mtx The meta-transaction.
                          /// @return mtxHash The EIP712 hash of `mtx`.
                          function getMetaTransactionHash(MetaTransactionData memory mtx)
                              public
                              override
                              view
                              returns (bytes32 mtxHash)
                          {
                              return _getEIP712Hash(keccak256(abi.encode(
                                  MTX_EIP712_TYPEHASH,
                                  mtx.signer,
                                  mtx.sender,
                                  mtx.minGasPrice,
                                  mtx.maxGasPrice,
                                  mtx.expirationTime,
                                  mtx.salt,
                                  keccak256(mtx.callData),
                                  mtx.value,
                                  mtx.feeToken,
                                  mtx.feeAmount
                              )));
                          }
                          /// @dev Execute a meta-transaction by `sender`. Low-level, hidden variant.
                          /// @param sender Who is executing the meta-transaction..
                          /// @param mtx The meta-transaction.
                          /// @param signature The signature by `mtx.signer`.
                          /// @return returnData The ABI-encoded result of the underlying call.
                          function _executeMetaTransactionPrivate(
                              address sender,
                              MetaTransactionData memory mtx,
                              bytes memory signature
                          )
                              private
                              returns (bytes memory returnData)
                          {
                              ExecuteState memory state;
                              state.sender = sender;
                              state.hash = getMetaTransactionHash(mtx);
                              state.mtx = mtx;
                              state.signature = signature;
                              _validateMetaTransaction(state);
                              // Mark the transaction executed.
                              assert(block.number > 0);
                              LibMetaTransactionsStorage.getStorage()
                                  .mtxHashToExecutedBlockNumber[state.hash] = block.number;
                              // Execute the call based on the selector.
                              state.selector = mtx.callData.readBytes4(0);
                              if (state.selector == ITransformERC20.transformERC20.selector) {
                                  returnData = _executeTransformERC20Call(state);
                              } else {
                                  LibMetaTransactionsRichErrors
                                      .MetaTransactionUnsupportedFunctionError(state.hash, state.selector)
                                      .rrevert();
                              }
                              // Pay the fee to the sender.
                              if (mtx.feeAmount > 0) {
                                  ITokenSpender(address(this))._spendERC20Tokens(
                                      mtx.feeToken,
                                      mtx.signer, // From the signer.
                                      sender, // To the sender.
                                      mtx.feeAmount
                                  );
                              }
                              emit MetaTransactionExecuted(
                                  state.hash,
                                  state.selector,
                                  mtx.signer,
                                  mtx.sender
                              );
                          }
                          /// @dev Validate that a meta-transaction is executable.
                          function _validateMetaTransaction(ExecuteState memory state)
                              private
                              view
                          {
                              // Must be from the required sender, if set.
                              if (state.mtx.sender != address(0) && state.mtx.sender != state.sender) {
                                  LibMetaTransactionsRichErrors
                                      .MetaTransactionWrongSenderError(
                                          state.hash,
                                          state.sender,
                                          state.mtx.sender
                                      ).rrevert();
                              }
                              // Must not be expired.
                              if (state.mtx.expirationTime <= block.timestamp) {
                                  LibMetaTransactionsRichErrors
                                      .MetaTransactionExpiredError(
                                          state.hash,
                                          block.timestamp,
                                          state.mtx.expirationTime
                                      ).rrevert();
                              }
                              // Must have a valid gas price.
                              if (state.mtx.minGasPrice > tx.gasprice || state.mtx.maxGasPrice < tx.gasprice) {
                                  LibMetaTransactionsRichErrors
                                      .MetaTransactionGasPriceError(
                                          state.hash,
                                          tx.gasprice,
                                          state.mtx.minGasPrice,
                                          state.mtx.maxGasPrice
                                      ).rrevert();
                              }
                              // Must have enough ETH.
                              state.selfBalance  = address(this).balance;
                              if (state.mtx.value > state.selfBalance) {
                                  LibMetaTransactionsRichErrors
                                      .MetaTransactionInsufficientEthError(
                                          state.hash,
                                          state.selfBalance,
                                          state.mtx.value
                                      ).rrevert();
                              }
                              // Must be signed by signer.
                              try
                                  ISignatureValidator(address(this))
                                      .validateHashSignature(state.hash, state.mtx.signer, state.signature)
                              {}
                              catch (bytes memory err) {
                                  LibMetaTransactionsRichErrors
                                      .MetaTransactionInvalidSignatureError(
                                          state.hash,
                                          state.signature,
                                          err
                                      ).rrevert();
                              }
                              // Transaction must not have been already executed.
                              state.executedBlockNumber = LibMetaTransactionsStorage
                                  .getStorage().mtxHashToExecutedBlockNumber[state.hash];
                              if (state.executedBlockNumber != 0) {
                                  LibMetaTransactionsRichErrors
                                      .MetaTransactionAlreadyExecutedError(
                                          state.hash,
                                          state.executedBlockNumber
                                      ).rrevert();
                              }
                          }
                          /// @dev Execute a `ITransformERC20.transformERC20()` meta-transaction call
                          ///      by decoding the call args and translating the call to the internal
                          ///      `ITransformERC20._transformERC20()` variant, where we can override
                          ///      the taker address.
                          function _executeTransformERC20Call(ExecuteState memory state)
                              private
                              returns (bytes memory returnData)
                          {
                              // HACK(dorothy-zbornak): `abi.decode()` with the individual args
                              // will cause a stack overflow. But we can prefix the call data with an
                              // offset to transform it into the encoding for the equivalent single struct arg.
                              // Decoding a single struct consumes far less stack space.
                              TransformERC20Args memory args;
                              {
                                  bytes memory encodedStructArgs = new bytes(state.mtx.callData.length - 4 + 32);
                                  // Copy the args data from the original, after the new struct offset prefix.
                                  bytes memory fromCallData = state.mtx.callData;
                                  assert(fromCallData.length >= 4);
                                  uint256 fromMem;
                                  uint256 toMem;
                                  assembly {
                                      // Prefix the original calldata with a struct offset,
                                      // which is just one word over.
                                      mstore(add(encodedStructArgs, 32), 32)
                                      // Copy everything after the selector.
                                      fromMem := add(fromCallData, 36)
                                      // Start copying after the struct offset.
                                      toMem := add(encodedStructArgs, 64)
                                  }
                                  LibBytesV06.memCopy(toMem, fromMem, fromCallData.length - 4);
                                  // Decode call args for `ITransformERC20.transformERC20()` as a struct.
                                  args = abi.decode(encodedStructArgs, (TransformERC20Args));
                              }
                              // Call `ITransformERC20._transformERC20()` (internal variant).
                              return _callSelf(
                                  state.hash,
                                  abi.encodeWithSelector(
                                      ITransformERC20._transformERC20.selector,
                                      keccak256(state.mtx.callData),
                                      state.mtx.signer, // taker is mtx signer
                                      args.inputToken,
                                      args.outputToken,
                                      args.inputTokenAmount,
                                      args.minOutputTokenAmount,
                                      args.transformations
                                  ),
                                  state.mtx.value
                              );
                          }
                          /// @dev Make an arbitrary internal, meta-transaction call.
                          ///      Warning: Do not let unadulerated `callData` into this function.
                          function _callSelf(bytes32 hash, bytes memory callData, uint256 value)
                              private
                              returns (bytes memory returnData)
                          {
                              bool success;
                              (success, returnData) = address(this).call{value: value}(callData);
                              if (!success) {
                                  LibMetaTransactionsRichErrors.MetaTransactionCallFailedError(
                                      hash,
                                      callData,
                                      returnData
                                  ).rrevert();
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../errors/LibCommonRichErrors.sol";
                      import "../errors/LibOwnableRichErrors.sol";
                      import "../features/IOwnable.sol";
                      import "../features/ISimpleFunctionRegistry.sol";
                      /// @dev Common feature utilities.
                      abstract contract FixinCommon {
                          using LibRichErrorsV06 for bytes;
                          /// @dev The implementation address of this feature.
                          address internal immutable _implementation;
                          /// @dev The caller must be this contract.
                          modifier onlySelf() virtual {
                              if (msg.sender != address(this)) {
                                  LibCommonRichErrors.OnlyCallableBySelfError(msg.sender).rrevert();
                              }
                              _;
                          }
                          /// @dev The caller of this function must be the owner.
                          modifier onlyOwner() virtual {
                              {
                                  address owner = IOwnable(address(this)).owner();
                                  if (msg.sender != owner) {
                                      LibOwnableRichErrors.OnlyOwnerError(
                                          msg.sender,
                                          owner
                                      ).rrevert();
                                  }
                              }
                              _;
                          }
                          constructor() internal {
                              // Remember this feature's original address.
                              _implementation = address(this);
                          }
                          /// @dev Registers a function implemented by this feature at `_implementation`.
                          ///      Can and should only be called within a `migrate()`.
                          /// @param selector The selector of the function whose implementation
                          ///        is at `_implementation`.
                          function _registerFeatureFunction(bytes4 selector)
                              internal
                          {
                              ISimpleFunctionRegistry(address(this)).extend(selector, _implementation);
                          }
                          /// @dev Encode a feature version as a `uint256`.
                          /// @param major The major version number of the feature.
                          /// @param minor The minor version number of the feature.
                          /// @param revision The revision number of the feature.
                          /// @return encodedVersion The encoded version number.
                          function _encodeVersion(uint32 major, uint32 minor, uint32 revision)
                              internal
                              pure
                              returns (uint256 encodedVersion)
                          {
                              return (major << 64) | (minor << 32) | revision;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../errors/LibCommonRichErrors.sol";
                      import "../errors/LibOwnableRichErrors.sol";
                      import "../features/IOwnable.sol";
                      /// @dev EIP712 helpers for features.
                      abstract contract FixinEIP712 {
                          /// @dev The domain hash separator for the entire exchange proxy.
                          bytes32 public immutable EIP712_DOMAIN_SEPARATOR;
                          constructor(address zeroExAddress) internal {
                              // Compute `EIP712_DOMAIN_SEPARATOR`
                              {
                                  uint256 chainId;
                                  assembly { chainId := chainid() }
                                  EIP712_DOMAIN_SEPARATOR = keccak256(
                                      abi.encode(
                                          keccak256(
                                              "EIP712Domain("
                                                  "string name,"
                                                  "string version,"
                                                  "uint256 chainId,"
                                                  "address verifyingContract"
                                              ")"
                                          ),
                                          keccak256("ZeroEx"),
                                          keccak256("1.0.0"),
                                          chainId,
                                          zeroExAddress
                                      )
                                  );
                              }
                          }
                          function _getEIP712Hash(bytes32 structHash)
                              internal
                              view
                              returns (bytes32 eip712Hash)
                          {
                              return keccak256(abi.encodePacked(
                                  hex"1901",
                                  EIP712_DOMAIN_SEPARATOR,
                                  structHash
                              ));
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../errors/LibOwnableRichErrors.sol";
                      library LibMigrate {
                          /// @dev Magic bytes returned by a migrator to indicate success.
                          ///      This is `keccack('MIGRATE_SUCCESS')`.
                          bytes4 internal constant MIGRATE_SUCCESS = 0x2c64c5ef;
                          using LibRichErrorsV06 for bytes;
                          /// @dev Perform a delegatecall and ensure it returns the magic bytes.
                          /// @param target The call target.
                          /// @param data The call data.
                          function delegatecallMigrateFunction(
                              address target,
                              bytes memory data
                          )
                              internal
                          {
                              (bool success, bytes memory resultData) = target.delegatecall(data);
                              if (!success ||
                                  resultData.length != 32 ||
                                  abi.decode(resultData, (bytes4)) != MIGRATE_SUCCESS)
                              {
                                  LibOwnableRichErrors.MigrateCallFailedError(target, resultData).rrevert();
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./LibStorage.sol";
                      /// @dev Storage helpers for the `MetaTransactions` feature.
                      library LibMetaTransactionsStorage {
                          /// @dev Storage bucket for this feature.
                          struct Storage {
                              // The block number when a hash was executed.
                              mapping (bytes32 => uint256) mtxHashToExecutedBlockNumber;
                          }
                          /// @dev Get the storage bucket for this contract.
                          function getStorage() internal pure returns (Storage storage stor) {
                              uint256 storageSlot = LibStorage.getStorageSlot(
                                  LibStorage.StorageId.MetaTransactions
                              );
                              // Dip into assembly to change the slot pointed to by the local
                              // variable `stor`.
                              // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
                              assembly { stor_slot := storageSlot }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../fixins/FixinCommon.sol";
                      import "../errors/LibOwnableRichErrors.sol";
                      import "../storage/LibOwnableStorage.sol";
                      import "../migrations/LibBootstrap.sol";
                      import "../migrations/LibMigrate.sol";
                      import "./IFeature.sol";
                      import "./IOwnable.sol";
                      import "./SimpleFunctionRegistry.sol";
                      /// @dev Owner management features.
                      contract Ownable is
                          IFeature,
                          IOwnable,
                          FixinCommon
                      {
                          /// @dev Name of this feature.
                          string public constant override FEATURE_NAME = "Ownable";
                          /// @dev Version of this feature.
                          uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
                          using LibRichErrorsV06 for bytes;
                          constructor() public FixinCommon() {
                              // solhint-disable-next-line no-empty-blocks
                          }
                          /// @dev Initializes this feature. The intial owner will be set to this (ZeroEx)
                          ///      to allow the bootstrappers to call `extend()`. Ownership should be
                          ///      transferred to the real owner by the bootstrapper after
                          ///      bootstrapping is complete.
                          /// @return success Magic bytes if successful.
                          function bootstrap() external returns (bytes4 success) {
                              // Set the owner to ourselves to allow bootstrappers to call `extend()`.
                              LibOwnableStorage.getStorage().owner = address(this);
                              // Register feature functions.
                              SimpleFunctionRegistry(address(this))._extendSelf(this.transferOwnership.selector, _implementation);
                              SimpleFunctionRegistry(address(this))._extendSelf(this.owner.selector, _implementation);
                              SimpleFunctionRegistry(address(this))._extendSelf(this.migrate.selector, _implementation);
                              return LibBootstrap.BOOTSTRAP_SUCCESS;
                          }
                          /// @dev Change the owner of this contract.
                          ///      Only directly callable by the owner.
                          /// @param newOwner New owner address.
                          function transferOwnership(address newOwner)
                              external
                              override
                              onlyOwner
                          {
                              LibOwnableStorage.Storage storage proxyStor = LibOwnableStorage.getStorage();
                              if (newOwner == address(0)) {
                                  LibOwnableRichErrors.TransferOwnerToZeroError().rrevert();
                              } else {
                                  proxyStor.owner = newOwner;
                                  emit OwnershipTransferred(msg.sender, newOwner);
                              }
                          }
                          /// @dev Execute a migration function in the context of the ZeroEx contract.
                          ///      The result of the function being called should be the magic bytes
                          ///      0x2c64c5ef (`keccack('MIGRATE_SUCCESS')`). Only callable by the owner.
                          ///      Temporarily sets the owner to ourselves so we can perform admin functions.
                          ///      Before returning, the owner will be set to `newOwner`.
                          /// @param target The migrator contract address.
                          /// @param data The call data.
                          /// @param newOwner The address of the new owner.
                          function migrate(address target, bytes calldata data, address newOwner)
                              external
                              override
                              onlyOwner
                          {
                              if (newOwner == address(0)) {
                                  LibOwnableRichErrors.TransferOwnerToZeroError().rrevert();
                              }
                              LibOwnableStorage.Storage storage stor = LibOwnableStorage.getStorage();
                              // The owner will be temporarily set to `address(this)` inside the call.
                              stor.owner = address(this);
                              // Perform the migration.
                              LibMigrate.delegatecallMigrateFunction(target, data);
                              // Update the owner.
                              stor.owner = newOwner;
                              emit Migrated(msg.sender, target, newOwner);
                          }
                          /// @dev Get the owner of this contract.
                          /// @return owner_ The owner of this contract.
                          function owner() external override view returns (address owner_) {
                              return LibOwnableStorage.getStorage().owner;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./LibStorage.sol";
                      /// @dev Storage helpers for the `Ownable` feature.
                      library LibOwnableStorage {
                          /// @dev Storage bucket for this feature.
                          struct Storage {
                              // The owner of this contract.
                              address owner;
                          }
                          /// @dev Get the storage bucket for this contract.
                          function getStorage() internal pure returns (Storage storage stor) {
                              uint256 storageSlot = LibStorage.getStorageSlot(
                                  LibStorage.StorageId.Ownable
                              );
                              // Dip into assembly to change the slot pointed to by the local
                              // variable `stor`.
                              // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
                              assembly { stor_slot := storageSlot }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../fixins/FixinCommon.sol";
                      import "../storage/LibProxyStorage.sol";
                      import "../storage/LibSimpleFunctionRegistryStorage.sol";
                      import "../errors/LibSimpleFunctionRegistryRichErrors.sol";
                      import "../migrations/LibBootstrap.sol";
                      import "./IFeature.sol";
                      import "./ISimpleFunctionRegistry.sol";
                      /// @dev Basic registry management features.
                      contract SimpleFunctionRegistry is
                          IFeature,
                          ISimpleFunctionRegistry,
                          FixinCommon
                      {
                          // solhint-disable
                          string public constant override FEATURE_NAME = "SimpleFunctionRegistry";
                          /// @dev Version of this feature.
                          uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
                          using LibRichErrorsV06 for bytes;
                          constructor() public FixinCommon() {
                              // solhint-disable-next-line no-empty-blocks
                          }
                          /// @dev Initializes this feature, registering its own functions.
                          /// @return success Magic bytes if successful.
                          function bootstrap()
                              external
                              returns (bytes4 success)
                          {
                              // Register the registration functions (inception vibes).
                              _extend(this.extend.selector, _implementation);
                              _extend(this._extendSelf.selector, _implementation);
                              // Register the rollback function.
                              _extend(this.rollback.selector, _implementation);
                              // Register getters.
                              _extend(this.getRollbackLength.selector, _implementation);
                              _extend(this.getRollbackEntryAtIndex.selector, _implementation);
                              return LibBootstrap.BOOTSTRAP_SUCCESS;
                          }
                          /// @dev Roll back to a prior implementation of a function.
                          ///      Only directly callable by an authority.
                          /// @param selector The function selector.
                          /// @param targetImpl The address of an older implementation of the function.
                          function rollback(bytes4 selector, address targetImpl)
                              external
                              override
                              onlyOwner
                          {
                              (
                                  LibSimpleFunctionRegistryStorage.Storage storage stor,
                                  LibProxyStorage.Storage storage proxyStor
                              ) = _getStorages();
                              address currentImpl = proxyStor.impls[selector];
                              if (currentImpl == targetImpl) {
                                  // Do nothing if already at targetImpl.
                                  return;
                              }
                              // Walk history backwards until we find the target implementation.
                              address[] storage history = stor.implHistory[selector];
                              uint256 i = history.length;
                              for (; i > 0; --i) {
                                  address impl = history[i - 1];
                                  history.pop();
                                  if (impl == targetImpl) {
                                      break;
                                  }
                              }
                              if (i == 0) {
                                  LibSimpleFunctionRegistryRichErrors.NotInRollbackHistoryError(
                                      selector,
                                      targetImpl
                                  ).rrevert();
                              }
                              proxyStor.impls[selector] = targetImpl;
                              emit ProxyFunctionUpdated(selector, currentImpl, targetImpl);
                          }
                          /// @dev Register or replace a function.
                          ///      Only directly callable by an authority.
                          /// @param selector The function selector.
                          /// @param impl The implementation contract for the function.
                          function extend(bytes4 selector, address impl)
                              external
                              override
                              onlyOwner
                          {
                              _extend(selector, impl);
                          }
                          /// @dev Register or replace a function.
                          ///      Only callable from within.
                          ///      This function is only used during the bootstrap process and
                          ///      should be deregistered by the deployer after bootstrapping is
                          ///      complete.
                          /// @param selector The function selector.
                          /// @param impl The implementation contract for the function.
                          function _extendSelf(bytes4 selector, address impl)
                              external
                              onlySelf
                          {
                              _extend(selector, impl);
                          }
                          /// @dev Retrieve the length of the rollback history for a function.
                          /// @param selector The function selector.
                          /// @return rollbackLength The number of items in the rollback history for
                          ///         the function.
                          function getRollbackLength(bytes4 selector)
                              external
                              override
                              view
                              returns (uint256 rollbackLength)
                          {
                              return LibSimpleFunctionRegistryStorage.getStorage().implHistory[selector].length;
                          }
                          /// @dev Retrieve an entry in the rollback history for a function.
                          /// @param selector The function selector.
                          /// @param idx The index in the rollback history.
                          /// @return impl An implementation address for the function at
                          ///         index `idx`.
                          function getRollbackEntryAtIndex(bytes4 selector, uint256 idx)
                              external
                              override
                              view
                              returns (address impl)
                          {
                              return LibSimpleFunctionRegistryStorage.getStorage().implHistory[selector][idx];
                          }
                          /// @dev Register or replace a function.
                          /// @param selector The function selector.
                          /// @param impl The implementation contract for the function.
                          function _extend(bytes4 selector, address impl)
                              private
                          {
                              (
                                  LibSimpleFunctionRegistryStorage.Storage storage stor,
                                  LibProxyStorage.Storage storage proxyStor
                              ) = _getStorages();
                              address oldImpl = proxyStor.impls[selector];
                              address[] storage history = stor.implHistory[selector];
                              history.push(oldImpl);
                              proxyStor.impls[selector] = impl;
                              emit ProxyFunctionUpdated(selector, oldImpl, impl);
                          }
                          /// @dev Get the storage buckets for this feature and the proxy.
                          /// @return stor Storage bucket for this feature.
                          /// @return proxyStor age bucket for the proxy.
                          function _getStorages()
                              private
                              pure
                              returns (
                                  LibSimpleFunctionRegistryStorage.Storage storage stor,
                                  LibProxyStorage.Storage storage proxyStor
                              )
                          {
                              return (
                                  LibSimpleFunctionRegistryStorage.getStorage(),
                                  LibProxyStorage.getStorage()
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./LibStorage.sol";
                      /// @dev Storage helpers for the `SimpleFunctionRegistry` feature.
                      library LibSimpleFunctionRegistryStorage {
                          /// @dev Storage bucket for this feature.
                          struct Storage {
                              // Mapping of function selector -> implementation history.
                              mapping(bytes4 => address[]) implHistory;
                          }
                          /// @dev Get the storage bucket for this contract.
                          function getStorage() internal pure returns (Storage storage stor) {
                              uint256 storageSlot = LibStorage.getStorageSlot(
                                  LibStorage.StorageId.SimpleFunctionRegistry
                              );
                              // Dip into assembly to change the slot pointed to by the local
                              // variable `stor`.
                              // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
                              assembly { stor_slot := storageSlot }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
                      import "../errors/LibSignatureRichErrors.sol";
                      import "../fixins/FixinCommon.sol";
                      import "../migrations/LibMigrate.sol";
                      import "./ISignatureValidator.sol";
                      import "./IFeature.sol";
                      /// @dev Feature for validating signatures.
                      contract SignatureValidator is
                          IFeature,
                          ISignatureValidator,
                          FixinCommon
                      {
                          using LibBytesV06 for bytes;
                          using LibRichErrorsV06 for bytes;
                          /// @dev Name of this feature.
                          string public constant override FEATURE_NAME = "SignatureValidator";
                          /// @dev Version of this feature.
                          uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
                          constructor() public FixinCommon() {
                              // solhint-disable-next-line no-empty-blocks
                          }
                          /// @dev Initialize and register this feature.
                          ///      Should be delegatecalled by `Migrate.migrate()`.
                          /// @return success `LibMigrate.SUCCESS` on success.
                          function migrate()
                              external
                              returns (bytes4 success)
                          {
                              _registerFeatureFunction(this.validateHashSignature.selector);
                              _registerFeatureFunction(this.isValidHashSignature.selector);
                              return LibMigrate.MIGRATE_SUCCESS;
                          }
                          /// @dev Validate that `hash` was signed by `signer` given `signature`.
                          ///      Reverts otherwise.
                          /// @param hash The hash that was signed.
                          /// @param signer The signer of the hash.
                          /// @param signature The signature. The last byte of this signature should
                          ///        be a member of the `SignatureType` enum.
                          function validateHashSignature(
                              bytes32 hash,
                              address signer,
                              bytes memory signature
                          )
                              public
                              override
                              view
                          {
                              SignatureType signatureType = _readValidSignatureType(
                                  hash,
                                  signer,
                                  signature
                              );
                              // TODO: When we support non-hash signature types, assert that
                              // `signatureType` is only `EIP712` or `EthSign` here.
                              _validateHashSignatureTypes(
                                  signatureType,
                                  hash,
                                  signer,
                                  signature
                              );
                          }
                          /// @dev Check that `hash` was signed by `signer` given `signature`.
                          /// @param hash The hash that was signed.
                          /// @param signer The signer of the hash.
                          /// @param signature The signature. The last byte of this signature should
                          ///        be a member of the `SignatureType` enum.
                          /// @return isValid `true` on success.
                          function isValidHashSignature(
                              bytes32 hash,
                              address signer,
                              bytes calldata signature
                          )
                              external
                              view
                              override
                              returns (bool isValid)
                          {
                              try this.validateHashSignature(hash, signer, signature) {
                                  isValid = true;
                              } catch (bytes memory) {
                                  isValid = false;
                              }
                          }
                          /// @dev Validates a hash-only signature type. Low-level, hidden variant.
                          /// @param signatureType The type of signature to check.
                          /// @param hash The hash that was signed.
                          /// @param signer The signer of the hash.
                          /// @param signature The signature. The last byte of this signature should
                          ///        be a member of the `SignatureType` enum.
                          function _validateHashSignatureTypes(
                              SignatureType signatureType,
                              bytes32 hash,
                              address signer,
                              bytes memory signature
                          )
                              private
                              pure
                          {
                              address recovered = address(0);
                              if (signatureType == SignatureType.Invalid) {
                                  // Always invalid signature.
                                  // Like Illegal, this is always implicitly available and therefore
                                  // offered explicitly. It can be implicitly created by providing
                                  // a correctly formatted but incorrect signature.
                                  LibSignatureRichErrors.SignatureValidationError(
                                      LibSignatureRichErrors.SignatureValidationErrorCodes.ALWAYS_INVALID,
                                      hash,
                                      signer,
                                      signature
                                  ).rrevert();
                              } else if (signatureType == SignatureType.EIP712) {
                                  // Signature using EIP712
                                  if (signature.length != 66) {
                                      LibSignatureRichErrors.SignatureValidationError(
                                          LibSignatureRichErrors.SignatureValidationErrorCodes.INVALID_LENGTH,
                                          hash,
                                          signer,
                                          signature
                                      ).rrevert();
                                  }
                                  uint8 v = uint8(signature[0]);
                                  bytes32 r = signature.readBytes32(1);
                                  bytes32 s = signature.readBytes32(33);
                                  recovered = ecrecover(
                                      hash,
                                      v,
                                      r,
                                      s
                                  );
                              } else if (signatureType == SignatureType.EthSign) {
                                  // Signed using `eth_sign`
                                  if (signature.length != 66) {
                                      LibSignatureRichErrors.SignatureValidationError(
                                          LibSignatureRichErrors.SignatureValidationErrorCodes.INVALID_LENGTH,
                                          hash,
                                          signer,
                                          signature
                                      ).rrevert();
                                  }
                                  uint8 v = uint8(signature[0]);
                                  bytes32 r = signature.readBytes32(1);
                                  bytes32 s = signature.readBytes32(33);
                                  recovered = ecrecover(
                                      keccak256(abi.encodePacked(
                                          "\\x19Ethereum Signed Message:\
                      32",
                                          hash
                                      )),
                                      v,
                                      r,
                                      s
                                  );
                              } else {
                                  // This should never happen.
                                  revert('SignatureValidator/ILLEGAL_CODE_PATH');
                              }
                              if (recovered == address(0) || signer != recovered) {
                                  LibSignatureRichErrors.SignatureValidationError(
                                      LibSignatureRichErrors.SignatureValidationErrorCodes.WRONG_SIGNER,
                                      hash,
                                      signer,
                                      signature
                                  ).rrevert();
                              }
                          }
                          /// @dev Reads the `SignatureType` from the end of a signature and validates it.
                          function _readValidSignatureType(
                              bytes32 hash,
                              address signer,
                              bytes memory signature
                          )
                              private
                              pure
                              returns (SignatureType signatureType)
                          {
                              // Read the signatureType from the signature
                              signatureType = _readSignatureType(
                                  hash,
                                  signer,
                                  signature
                              );
                              // Ensure signature is supported
                              if (uint8(signatureType) >= uint8(SignatureType.NSignatureTypes)) {
                                  LibSignatureRichErrors.SignatureValidationError(
                                      LibSignatureRichErrors.SignatureValidationErrorCodes.UNSUPPORTED,
                                      hash,
                                      signer,
                                      signature
                                  ).rrevert();
                              }
                              // Always illegal signature.
                              // This is always an implicit option since a signer can create a
                              // signature array with invalid type or length. We may as well make
                              // it an explicit option. This aids testing and analysis. It is
                              // also the initialization value for the enum type.
                              if (signatureType == SignatureType.Illegal) {
                                  LibSignatureRichErrors.SignatureValidationError(
                                      LibSignatureRichErrors.SignatureValidationErrorCodes.ILLEGAL,
                                      hash,
                                      signer,
                                      signature
                                  ).rrevert();
                              }
                          }
                          /// @dev Reads the `SignatureType` from the end of a signature.
                          function _readSignatureType(
                              bytes32 hash,
                              address signer,
                              bytes memory signature
                          )
                              private
                              pure
                              returns (SignatureType sigType)
                          {
                              if (signature.length == 0) {
                                  LibSignatureRichErrors.SignatureValidationError(
                                      LibSignatureRichErrors.SignatureValidationErrorCodes.INVALID_LENGTH,
                                      hash,
                                      signer,
                                      signature
                                  ).rrevert();
                              }
                              return SignatureType(uint8(signature[signature.length - 1]));
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
                      import "../errors/LibSpenderRichErrors.sol";
                      import "../fixins/FixinCommon.sol";
                      import "../migrations/LibMigrate.sol";
                      import "../external/IAllowanceTarget.sol";
                      import "../storage/LibTokenSpenderStorage.sol";
                      import "./ITokenSpender.sol";
                      import "./IFeature.sol";
                      import "./ISimpleFunctionRegistry.sol";
                      /// @dev Feature that allows spending token allowances.
                      contract TokenSpender is
                          IFeature,
                          ITokenSpender,
                          FixinCommon
                      {
                          // solhint-disable
                          /// @dev Name of this feature.
                          string public constant override FEATURE_NAME = "TokenSpender";
                          /// @dev Version of this feature.
                          uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
                          // solhint-enable
                          using LibRichErrorsV06 for bytes;
                          constructor() public FixinCommon() {
                              // solhint-disable-next-line no-empty-blocks
                          }
                          /// @dev Initialize and register this feature. Should be delegatecalled
                          ///      into during a `Migrate.migrate()`.
                          /// @param allowanceTarget An `allowanceTarget` instance, configured to have
                          ///        the ZeroeEx contract as an authority.
                          /// @return success `MIGRATE_SUCCESS` on success.
                          function migrate(IAllowanceTarget allowanceTarget)
                              external
                              returns (bytes4 success)
                          {
                              LibTokenSpenderStorage.getStorage().allowanceTarget = allowanceTarget;
                              _registerFeatureFunction(this.getAllowanceTarget.selector);
                              _registerFeatureFunction(this._spendERC20Tokens.selector);
                              _registerFeatureFunction(this.getSpendableERC20BalanceOf.selector);
                              return LibMigrate.MIGRATE_SUCCESS;
                          }
                          /// @dev Transfers ERC20 tokens from `owner` to `to`. Only callable from within.
                          /// @param token The token to spend.
                          /// @param owner The owner of the tokens.
                          /// @param to The recipient of the tokens.
                          /// @param amount The amount of `token` to transfer.
                          function _spendERC20Tokens(
                              IERC20TokenV06 token,
                              address owner,
                              address to,
                              uint256 amount
                          )
                              external
                              override
                              onlySelf
                          {
                              IAllowanceTarget spender = LibTokenSpenderStorage.getStorage().allowanceTarget;
                              // Have the allowance target execute an ERC20 `transferFrom()`.
                              (bool didSucceed, bytes memory resultData) = address(spender).call(
                                  abi.encodeWithSelector(
                                      IAllowanceTarget.executeCall.selector,
                                      address(token),
                                      abi.encodeWithSelector(
                                          IERC20TokenV06.transferFrom.selector,
                                          owner,
                                          to,
                                          amount
                                      )
                                  )
                              );
                              if (didSucceed) {
                                  resultData = abi.decode(resultData, (bytes));
                              }
                              if (!didSucceed || !LibERC20TokenV06.isSuccessfulResult(resultData)) {
                                  LibSpenderRichErrors.SpenderERC20TransferFromFailedError(
                                      address(token),
                                      owner,
                                      to,
                                      amount,
                                      resultData
                                  ).rrevert();
                              }
                          }
                          /// @dev Gets the maximum amount of an ERC20 token `token` that can be
                          ///      pulled from `owner` by the token spender.
                          /// @param token The token to spend.
                          /// @param owner The owner of the tokens.
                          /// @return amount The amount of tokens that can be pulled.
                          function getSpendableERC20BalanceOf(IERC20TokenV06 token, address owner)
                              external
                              override
                              view
                              returns (uint256 amount)
                          {
                              return LibSafeMathV06.min256(
                                  token.allowance(owner, address(LibTokenSpenderStorage.getStorage().allowanceTarget)),
                                  token.balanceOf(owner)
                              );
                          }
                          /// @dev Get the address of the allowance target.
                          /// @return target The target of token allowances.
                          function getAllowanceTarget()
                              external
                              override
                              view
                              returns (address target)
                          {
                              return address(LibTokenSpenderStorage.getStorage().allowanceTarget);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./errors/LibRichErrorsV06.sol";
                      import "./errors/LibSafeMathRichErrorsV06.sol";
                      library LibSafeMathV06 {
                          function safeMul(uint256 a, uint256 b)
                              internal
                              pure
                              returns (uint256)
                          {
                              if (a == 0) {
                                  return 0;
                              }
                              uint256 c = a * b;
                              if (c / a != b) {
                                  LibRichErrorsV06.rrevert(LibSafeMathRichErrorsV06.Uint256BinOpError(
                                      LibSafeMathRichErrorsV06.BinOpErrorCodes.MULTIPLICATION_OVERFLOW,
                                      a,
                                      b
                                  ));
                              }
                              return c;
                          }
                          function safeDiv(uint256 a, uint256 b)
                              internal
                              pure
                              returns (uint256)
                          {
                              if (b == 0) {
                                  LibRichErrorsV06.rrevert(LibSafeMathRichErrorsV06.Uint256BinOpError(
                                      LibSafeMathRichErrorsV06.BinOpErrorCodes.DIVISION_BY_ZERO,
                                      a,
                                      b
                                  ));
                              }
                              uint256 c = a / b;
                              return c;
                          }
                          function safeSub(uint256 a, uint256 b)
                              internal
                              pure
                              returns (uint256)
                          {
                              if (b > a) {
                                  LibRichErrorsV06.rrevert(LibSafeMathRichErrorsV06.Uint256BinOpError(
                                      LibSafeMathRichErrorsV06.BinOpErrorCodes.SUBTRACTION_UNDERFLOW,
                                      a,
                                      b
                                  ));
                              }
                              return a - b;
                          }
                          function safeAdd(uint256 a, uint256 b)
                              internal
                              pure
                              returns (uint256)
                          {
                              uint256 c = a + b;
                              if (c < a) {
                                  LibRichErrorsV06.rrevert(LibSafeMathRichErrorsV06.Uint256BinOpError(
                                      LibSafeMathRichErrorsV06.BinOpErrorCodes.ADDITION_OVERFLOW,
                                      a,
                                      b
                                  ));
                              }
                              return c;
                          }
                          function max256(uint256 a, uint256 b)
                              internal
                              pure
                              returns (uint256)
                          {
                              return a >= b ? a : b;
                          }
                          function min256(uint256 a, uint256 b)
                              internal
                              pure
                              returns (uint256)
                          {
                              return a < b ? a : b;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibSafeMathRichErrorsV06 {
                          // bytes4(keccak256("Uint256BinOpError(uint8,uint256,uint256)"))
                          bytes4 internal constant UINT256_BINOP_ERROR_SELECTOR =
                              0xe946c1bb;
                          // bytes4(keccak256("Uint256DowncastError(uint8,uint256)"))
                          bytes4 internal constant UINT256_DOWNCAST_ERROR_SELECTOR =
                              0xc996af7b;
                          enum BinOpErrorCodes {
                              ADDITION_OVERFLOW,
                              MULTIPLICATION_OVERFLOW,
                              SUBTRACTION_UNDERFLOW,
                              DIVISION_BY_ZERO
                          }
                          enum DowncastErrorCodes {
                              VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT32,
                              VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT64,
                              VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT96
                          }
                          // solhint-disable func-name-mixedcase
                          function Uint256BinOpError(
                              BinOpErrorCodes errorCode,
                              uint256 a,
                              uint256 b
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  UINT256_BINOP_ERROR_SELECTOR,
                                  errorCode,
                                  a,
                                  b
                              );
                          }
                          function Uint256DowncastError(
                              DowncastErrorCodes errorCode,
                              uint256 a
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  UINT256_DOWNCAST_ERROR_SELECTOR,
                                  errorCode,
                                  a
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
                      import "./IERC20TokenV06.sol";
                      library LibERC20TokenV06 {
                          bytes constant private DECIMALS_CALL_DATA = hex"313ce567";
                          /// @dev Calls `IERC20TokenV06(token).approve()`.
                          ///      Reverts if the result fails `isSuccessfulResult()` or the call reverts.
                          /// @param token The address of the token contract.
                          /// @param spender The address that receives an allowance.
                          /// @param allowance The allowance to set.
                          function compatApprove(
                              IERC20TokenV06 token,
                              address spender,
                              uint256 allowance
                          )
                              internal
                          {
                              bytes memory callData = abi.encodeWithSelector(
                                  token.approve.selector,
                                  spender,
                                  allowance
                              );
                              _callWithOptionalBooleanResult(address(token), callData);
                          }
                          /// @dev Calls `IERC20TokenV06(token).approve()` and sets the allowance to the
                          ///      maximum if the current approval is not already >= an amount.
                          ///      Reverts if the result fails `isSuccessfulResult()` or the call reverts.
                          /// @param token The address of the token contract.
                          /// @param spender The address that receives an allowance.
                          /// @param amount The minimum allowance needed.
                          function approveIfBelow(
                              IERC20TokenV06 token,
                              address spender,
                              uint256 amount
                          )
                              internal
                          {
                              if (token.allowance(address(this), spender) < amount) {
                                  compatApprove(token, spender, uint256(-1));
                              }
                          }
                          /// @dev Calls `IERC20TokenV06(token).transfer()`.
                          ///      Reverts if the result fails `isSuccessfulResult()` or the call reverts.
                          /// @param token The address of the token contract.
                          /// @param to The address that receives the tokens
                          /// @param amount Number of tokens to transfer.
                          function compatTransfer(
                              IERC20TokenV06 token,
                              address to,
                              uint256 amount
                          )
                              internal
                          {
                              bytes memory callData = abi.encodeWithSelector(
                                  token.transfer.selector,
                                  to,
                                  amount
                              );
                              _callWithOptionalBooleanResult(address(token), callData);
                          }
                          /// @dev Calls `IERC20TokenV06(token).transferFrom()`.
                          ///      Reverts if the result fails `isSuccessfulResult()` or the call reverts.
                          /// @param token The address of the token contract.
                          /// @param from The owner of the tokens.
                          /// @param to The address that receives the tokens
                          /// @param amount Number of tokens to transfer.
                          function compatTransferFrom(
                              IERC20TokenV06 token,
                              address from,
                              address to,
                              uint256 amount
                          )
                              internal
                          {
                              bytes memory callData = abi.encodeWithSelector(
                                  token.transferFrom.selector,
                                  from,
                                  to,
                                  amount
                              );
                              _callWithOptionalBooleanResult(address(token), callData);
                          }
                          /// @dev Retrieves the number of decimals for a token.
                          ///      Returns `18` if the call reverts.
                          /// @param token The address of the token contract.
                          /// @return tokenDecimals The number of decimals places for the token.
                          function compatDecimals(IERC20TokenV06 token)
                              internal
                              view
                              returns (uint8 tokenDecimals)
                          {
                              tokenDecimals = 18;
                              (bool didSucceed, bytes memory resultData) = address(token).staticcall(DECIMALS_CALL_DATA);
                              if (didSucceed && resultData.length == 32) {
                                  tokenDecimals = uint8(LibBytesV06.readUint256(resultData, 0));
                              }
                          }
                          /// @dev Retrieves the allowance for a token, owner, and spender.
                          ///      Returns `0` if the call reverts.
                          /// @param token The address of the token contract.
                          /// @param owner The owner of the tokens.
                          /// @param spender The address the spender.
                          /// @return allowance_ The allowance for a token, owner, and spender.
                          function compatAllowance(IERC20TokenV06 token, address owner, address spender)
                              internal
                              view
                              returns (uint256 allowance_)
                          {
                              (bool didSucceed, bytes memory resultData) = address(token).staticcall(
                                  abi.encodeWithSelector(
                                      token.allowance.selector,
                                      owner,
                                      spender
                                  )
                              );
                              if (didSucceed && resultData.length == 32) {
                                  allowance_ = LibBytesV06.readUint256(resultData, 0);
                              }
                          }
                          /// @dev Retrieves the balance for a token owner.
                          ///      Returns `0` if the call reverts.
                          /// @param token The address of the token contract.
                          /// @param owner The owner of the tokens.
                          /// @return balance The token balance of an owner.
                          function compatBalanceOf(IERC20TokenV06 token, address owner)
                              internal
                              view
                              returns (uint256 balance)
                          {
                              (bool didSucceed, bytes memory resultData) = address(token).staticcall(
                                  abi.encodeWithSelector(
                                      token.balanceOf.selector,
                                      owner
                                  )
                              );
                              if (didSucceed && resultData.length == 32) {
                                  balance = LibBytesV06.readUint256(resultData, 0);
                              }
                          }
                          /// @dev Check if the data returned by a non-static call to an ERC20 token
                          ///      is a successful result. Supported functions are `transfer()`,
                          ///      `transferFrom()`, and `approve()`.
                          /// @param resultData The raw data returned by a non-static call to the ERC20 token.
                          /// @return isSuccessful Whether the result data indicates success.
                          function isSuccessfulResult(bytes memory resultData)
                              internal
                              pure
                              returns (bool isSuccessful)
                          {
                              if (resultData.length == 0) {
                                  return true;
                              }
                              if (resultData.length == 32) {
                                  uint256 result = LibBytesV06.readUint256(resultData, 0);
                                  if (result == 1) {
                                      return true;
                                  }
                              }
                          }
                          /// @dev Executes a call on address `target` with calldata `callData`
                          ///      and asserts that either nothing was returned or a single boolean
                          ///      was returned equal to `true`.
                          /// @param target The call target.
                          /// @param callData The abi-encoded call data.
                          function _callWithOptionalBooleanResult(
                              address target,
                              bytes memory callData
                          )
                              private
                          {
                              (bool didSucceed, bytes memory resultData) = target.call(callData);
                              if (didSucceed && isSuccessfulResult(resultData)) {
                                  return;
                              }
                              LibRichErrorsV06.rrevert(resultData);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./LibStorage.sol";
                      import "../external/IAllowanceTarget.sol";
                      /// @dev Storage helpers for the `TokenSpender` feature.
                      library LibTokenSpenderStorage {
                          /// @dev Storage bucket for this feature.
                          struct Storage {
                              // Allowance target contract.
                              IAllowanceTarget allowanceTarget;
                          }
                          /// @dev Get the storage bucket for this contract.
                          function getStorage() internal pure returns (Storage storage stor) {
                              uint256 storageSlot = LibStorage.getStorageSlot(
                                  LibStorage.StorageId.TokenSpender
                              );
                              // Dip into assembly to change the slot pointed to by the local
                              // variable `stor`.
                              // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
                              assembly { stor_slot := storageSlot }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "../errors/LibTransformERC20RichErrors.sol";
                      import "../fixins/FixinCommon.sol";
                      import "../migrations/LibMigrate.sol";
                      import "../external/IFlashWallet.sol";
                      import "../external/FlashWallet.sol";
                      import "../storage/LibTransformERC20Storage.sol";
                      import "../transformers/IERC20Transformer.sol";
                      import "../transformers/LibERC20Transformer.sol";
                      import "./ITransformERC20.sol";
                      import "./ITokenSpender.sol";
                      import "./IFeature.sol";
                      import "./ISimpleFunctionRegistry.sol";
                      /// @dev Feature to composably transform between ERC20 tokens.
                      contract TransformERC20 is
                          IFeature,
                          ITransformERC20,
                          FixinCommon
                      {
                          /// @dev Stack vars for `_transformERC20Private()`.
                          struct TransformERC20PrivateState {
                              IFlashWallet wallet;
                              address transformerDeployer;
                              uint256 takerOutputTokenBalanceBefore;
                              uint256 takerOutputTokenBalanceAfter;
                          }
                          /// @dev Name of this feature.
                          string public constant override FEATURE_NAME = "TransformERC20";
                          /// @dev Version of this feature.
                          uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0);
                          using LibSafeMathV06 for uint256;
                          using LibRichErrorsV06 for bytes;
                          constructor() public FixinCommon() {
                              // solhint-disable-next-line no-empty-blocks
                          }
                          /// @dev Initialize and register this feature.
                          ///      Should be delegatecalled by `Migrate.migrate()`.
                          /// @param transformerDeployer The trusted deployer for transformers.
                          /// @return success `LibMigrate.SUCCESS` on success.
                          function migrate(address transformerDeployer)
                              external
                              returns (bytes4 success)
                          {
                              _registerFeatureFunction(this.getTransformerDeployer.selector);
                              _registerFeatureFunction(this.createTransformWallet.selector);
                              _registerFeatureFunction(this.getTransformWallet.selector);
                              _registerFeatureFunction(this.setTransformerDeployer.selector);
                              _registerFeatureFunction(this.transformERC20.selector);
                              _registerFeatureFunction(this._transformERC20.selector);
                              this.createTransformWallet();
                              LibTransformERC20Storage.getStorage().transformerDeployer = transformerDeployer;
                              return LibMigrate.MIGRATE_SUCCESS;
                          }
                          /// @dev Replace the allowed deployer for transformers.
                          ///      Only callable by the owner.
                          /// @param transformerDeployer The address of the trusted deployer for transformers.
                          function setTransformerDeployer(address transformerDeployer)
                              external
                              override
                              onlyOwner
                          {
                              LibTransformERC20Storage.getStorage().transformerDeployer = transformerDeployer;
                              emit TransformerDeployerUpdated(transformerDeployer);
                          }
                          /// @dev Return the allowed deployer for transformers.
                          /// @return deployer The transform deployer address.
                          function getTransformerDeployer()
                              public
                              override
                              view
                              returns (address deployer)
                          {
                              return LibTransformERC20Storage.getStorage().transformerDeployer;
                          }
                          /// @dev Deploy a new wallet instance and replace the current one with it.
                          ///      Useful if we somehow break the current wallet instance.
                          ///      Only callable by the owner.
                          /// @return wallet The new wallet instance.
                          function createTransformWallet()
                              public
                              override
                              onlyOwner
                              returns (IFlashWallet wallet)
                          {
                              wallet = new FlashWallet();
                              LibTransformERC20Storage.getStorage().wallet = wallet;
                          }
                          /// @dev Executes a series of transformations to convert an ERC20 `inputToken`
                          ///      to an ERC20 `outputToken`.
                          /// @param inputToken The token being provided by the sender.
                          ///        If `0xeee...`, ETH is implied and should be provided with the call.`
                          /// @param outputToken The token to be acquired by the sender.
                          ///        `0xeee...` implies ETH.
                          /// @param inputTokenAmount The amount of `inputToken` to take from the sender.
                          ///        If set to `uint256(-1)`, the entire spendable balance of the taker
                          ///        will be solt.
                          /// @param minOutputTokenAmount The minimum amount of `outputToken` the sender
                          ///        must receive for the entire transformation to succeed. If set to zero,
                          ///        the minimum output token transfer will not be asserted.
                          /// @param transformations The transformations to execute on the token balance(s)
                          ///        in sequence.
                          /// @return outputTokenAmount The amount of `outputToken` received by the sender.
                          function transformERC20(
                              IERC20TokenV06 inputToken,
                              IERC20TokenV06 outputToken,
                              uint256 inputTokenAmount,
                              uint256 minOutputTokenAmount,
                              Transformation[] memory transformations
                          )
                              public
                              override
                              payable
                              returns (uint256 outputTokenAmount)
                          {
                              return _transformERC20Private(
                                  keccak256(msg.data),
                                  msg.sender,
                                  inputToken,
                                  outputToken,
                                  inputTokenAmount,
                                  minOutputTokenAmount,
                                  transformations
                              );
                          }
                          /// @dev Internal version of `transformERC20()`. Only callable from within.
                          /// @param callDataHash Hash of the ingress calldata.
                          /// @param taker The taker address.
                          /// @param inputToken The token being provided by the taker.
                          ///        If `0xeee...`, ETH is implied and should be provided with the call.`
                          /// @param outputToken The token to be acquired by the taker.
                          ///        `0xeee...` implies ETH.
                          /// @param inputTokenAmount The amount of `inputToken` to take from the taker.
                          ///        If set to `uint256(-1)`, the entire spendable balance of the taker
                          ///        will be solt.
                          /// @param minOutputTokenAmount The minimum amount of `outputToken` the taker
                          ///        must receive for the entire transformation to succeed. If set to zero,
                          ///        the minimum output token transfer will not be asserted.
                          /// @param transformations The transformations to execute on the token balance(s)
                          ///        in sequence.
                          /// @return outputTokenAmount The amount of `outputToken` received by the taker.
                          function _transformERC20(
                              bytes32 callDataHash,
                              address payable taker,
                              IERC20TokenV06 inputToken,
                              IERC20TokenV06 outputToken,
                              uint256 inputTokenAmount,
                              uint256 minOutputTokenAmount,
                              Transformation[] memory transformations
                          )
                              public
                              virtual
                              override
                              payable
                              onlySelf
                              returns (uint256 outputTokenAmount)
                          {
                              return _transformERC20Private(
                                  callDataHash,
                                  taker,
                                  inputToken,
                                  outputToken,
                                  inputTokenAmount,
                                  minOutputTokenAmount,
                                  transformations
                              );
                          }
                          /// @dev Private version of `transformERC20()`.
                          /// @param callDataHash Hash of the ingress calldata.
                          /// @param taker The taker address.
                          /// @param inputToken The token being provided by the taker.
                          ///        If `0xeee...`, ETH is implied and should be provided with the call.`
                          /// @param outputToken The token to be acquired by the taker.
                          ///        `0xeee...` implies ETH.
                          /// @param inputTokenAmount The amount of `inputToken` to take from the taker.
                          ///        If set to `uint256(-1)`, the entire spendable balance of the taker
                          ///        will be solt.
                          /// @param minOutputTokenAmount The minimum amount of `outputToken` the taker
                          ///        must receive for the entire transformation to succeed. If set to zero,
                          ///        the minimum output token transfer will not be asserted.
                          /// @param transformations The transformations to execute on the token balance(s)
                          ///        in sequence.
                          /// @return outputTokenAmount The amount of `outputToken` received by the taker.
                          function _transformERC20Private(
                              bytes32 callDataHash,
                              address payable taker,
                              IERC20TokenV06 inputToken,
                              IERC20TokenV06 outputToken,
                              uint256 inputTokenAmount,
                              uint256 minOutputTokenAmount,
                              Transformation[] memory transformations
                          )
                              private
                              returns (uint256 outputTokenAmount)
                          {
                              // If the input token amount is -1, transform the taker's entire
                              // spendable balance.
                              if (inputTokenAmount == uint256(-1)) {
                                  inputTokenAmount = ITokenSpender(address(this))
                                      .getSpendableERC20BalanceOf(inputToken, taker);
                              }
                              TransformERC20PrivateState memory state;
                              state.wallet = getTransformWallet();
                              state.transformerDeployer = getTransformerDeployer();
                              // Remember the initial output token balance of the taker.
                              state.takerOutputTokenBalanceBefore =
                                  LibERC20Transformer.getTokenBalanceOf(outputToken, taker);
                              // Pull input tokens from the taker to the wallet and transfer attached ETH.
                              _transferInputTokensAndAttachedEth(
                                  inputToken,
                                  taker,
                                  address(state.wallet),
                                  inputTokenAmount
                              );
                              // Perform transformations.
                              for (uint256 i = 0; i < transformations.length; ++i) {
                                  _executeTransformation(
                                      state.wallet,
                                      transformations[i],
                                      state.transformerDeployer,
                                      taker,
                                      callDataHash
                                  );
                              }
                              // Compute how much output token has been transferred to the taker.
                              state.takerOutputTokenBalanceAfter =
                                  LibERC20Transformer.getTokenBalanceOf(outputToken, taker);
                              if (state.takerOutputTokenBalanceAfter > state.takerOutputTokenBalanceBefore) {
                                  outputTokenAmount = state.takerOutputTokenBalanceAfter.safeSub(
                                      state.takerOutputTokenBalanceBefore
                                  );
                              } else if (state.takerOutputTokenBalanceAfter < state.takerOutputTokenBalanceBefore) {
                                  LibTransformERC20RichErrors.NegativeTransformERC20OutputError(
                                      address(outputToken),
                                      state.takerOutputTokenBalanceBefore - state.takerOutputTokenBalanceAfter
                                  ).rrevert();
                              }
                              // Ensure enough output token has been sent to the taker.
                              if (outputTokenAmount < minOutputTokenAmount) {
                                  LibTransformERC20RichErrors.IncompleteTransformERC20Error(
                                      address(outputToken),
                                      outputTokenAmount,
                                      minOutputTokenAmount
                                  ).rrevert();
                              }
                              // Emit an event.
                              emit TransformedERC20(
                                  taker,
                                  address(inputToken),
                                  address(outputToken),
                                  inputTokenAmount,
                                  outputTokenAmount
                              );
                          }
                          /// @dev Return the current wallet instance that will serve as the execution
                          ///      context for transformations.
                          /// @return wallet The wallet instance.
                          function getTransformWallet()
                              public
                              override
                              view
                              returns (IFlashWallet wallet)
                          {
                              return LibTransformERC20Storage.getStorage().wallet;
                          }
                          /// @dev Transfer input tokens from the taker and any attached ETH to `to`
                          /// @param inputToken The token to pull from the taker.
                          /// @param from The from (taker) address.
                          /// @param to The recipient of tokens and ETH.
                          /// @param amount Amount of `inputToken` tokens to transfer.
                          function _transferInputTokensAndAttachedEth(
                              IERC20TokenV06 inputToken,
                              address from,
                              address payable to,
                              uint256 amount
                          )
                              private
                          {
                              // Transfer any attached ETH.
                              if (msg.value != 0) {
                                  to.transfer(msg.value);
                              }
                              // Transfer input tokens.
                              if (!LibERC20Transformer.isTokenETH(inputToken)) {
                                  // Token is not ETH, so pull ERC20 tokens.
                                  ITokenSpender(address(this))._spendERC20Tokens(
                                      inputToken,
                                      from,
                                      to,
                                      amount
                                  );
                              } else if (msg.value < amount) {
                                   // Token is ETH, so the caller must attach enough ETH to the call.
                                  LibTransformERC20RichErrors.InsufficientEthAttachedError(
                                      msg.value,
                                      amount
                                  ).rrevert();
                              }
                          }
                          /// @dev Executs a transformer in the context of `wallet`.
                          /// @param wallet The wallet instance.
                          /// @param transformation The transformation.
                          /// @param transformerDeployer The address of the transformer deployer.
                          /// @param taker The taker address.
                          /// @param callDataHash Hash of the calldata.
                          function _executeTransformation(
                              IFlashWallet wallet,
                              Transformation memory transformation,
                              address transformerDeployer,
                              address payable taker,
                              bytes32 callDataHash
                          )
                              private
                          {
                              // Derive the transformer address from the deployment nonce.
                              address payable transformer = LibERC20Transformer.getDeployedAddress(
                                  transformerDeployer,
                                  transformation.deploymentNonce
                              );
                              // Call `transformer.transform()` as the wallet.
                              bytes memory resultData = wallet.executeDelegateCall(
                                  // The call target.
                                  transformer,
                                  // Call data.
                                  abi.encodeWithSelector(
                                      IERC20Transformer.transform.selector,
                                      callDataHash,
                                      taker,
                                      transformation.data
                                  )
                              );
                              // Ensure the transformer returned the magic bytes.
                              if (resultData.length != 32 ||
                                  abi.decode(resultData, (bytes4)) != LibERC20Transformer.TRANSFORMER_SUCCESS
                              ) {
                                  LibTransformERC20RichErrors.TransformerFailedError(
                                      transformer,
                                      transformation.data,
                                      resultData
                                  ).rrevert();
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./LibStorage.sol";
                      import "../external/IFlashWallet.sol";
                      /// @dev Storage helpers for the `TransformERC20` feature.
                      library LibTransformERC20Storage {
                          /// @dev Storage bucket for this feature.
                          struct Storage {
                              // The current wallet instance.
                              IFlashWallet wallet;
                              // The transformer deployer address.
                              address transformerDeployer;
                          }
                          /// @dev Get the storage bucket for this contract.
                          function getStorage() internal pure returns (Storage storage stor) {
                              uint256 storageSlot = LibStorage.getStorageSlot(
                                  LibStorage.StorageId.TransformERC20
                              );
                              // Dip into assembly to change the slot pointed to by the local
                              // variable `stor`.
                              // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
                              assembly { stor_slot := storageSlot }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
                      library LibERC20Transformer {
                          using LibERC20TokenV06 for IERC20TokenV06;
                          /// @dev ETH pseudo-token address.
                          address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                          /// @dev Return value indicating success in `IERC20Transformer.transform()`.
                          ///      This is just `keccak256('TRANSFORMER_SUCCESS')`.
                          bytes4 constant internal TRANSFORMER_SUCCESS = 0x13c9929e;
                          /// @dev Transfer ERC20 tokens and ETH.
                          /// @param token An ERC20 or the ETH pseudo-token address (`ETH_TOKEN_ADDRESS`).
                          /// @param to The recipient.
                          /// @param amount The transfer amount.
                          function transformerTransfer(
                              IERC20TokenV06 token,
                              address payable to,
                              uint256 amount
                          )
                              internal
                          {
                              if (isTokenETH(token)) {
                                  to.transfer(amount);
                              } else {
                                  token.compatTransfer(to, amount);
                              }
                          }
                          /// @dev Check if a token is the ETH pseudo-token.
                          /// @param token The token to check.
                          /// @return isETH `true` if the token is the ETH pseudo-token.
                          function isTokenETH(IERC20TokenV06 token)
                              internal
                              pure
                              returns (bool isETH)
                          {
                              return address(token) == ETH_TOKEN_ADDRESS;
                          }
                          /// @dev Check the balance of an ERC20 token or ETH.
                          /// @param token An ERC20 or the ETH pseudo-token address (`ETH_TOKEN_ADDRESS`).
                          /// @param owner Holder of the tokens.
                          /// @return tokenBalance The balance of `owner`.
                          function getTokenBalanceOf(IERC20TokenV06 token, address owner)
                              internal
                              view
                              returns (uint256 tokenBalance)
                          {
                              if (isTokenETH(token)) {
                                  return owner.balance;
                              }
                              return token.balanceOf(owner);
                          }
                          /// @dev RLP-encode a 32-bit or less account nonce.
                          /// @param nonce A positive integer in the range 0 <= nonce < 2^32.
                          /// @return rlpNonce The RLP encoding.
                          function rlpEncodeNonce(uint32 nonce)
                              internal
                              pure
                              returns (bytes memory rlpNonce)
                          {
                              // See https://github.com/ethereum/wiki/wiki/RLP for RLP encoding rules.
                              if (nonce == 0) {
                                  rlpNonce = new bytes(1);
                                  rlpNonce[0] = 0x80;
                              } else if (nonce < 0x80) {
                                  rlpNonce = new bytes(1);
                                  rlpNonce[0] = byte(uint8(nonce));
                              } else if (nonce <= 0xFF) {
                                  rlpNonce = new bytes(2);
                                  rlpNonce[0] = 0x81;
                                  rlpNonce[1] = byte(uint8(nonce));
                              } else if (nonce <= 0xFFFF) {
                                  rlpNonce = new bytes(3);
                                  rlpNonce[0] = 0x82;
                                  rlpNonce[1] = byte(uint8((nonce & 0xFF00) >> 8));
                                  rlpNonce[2] = byte(uint8(nonce));
                              } else if (nonce <= 0xFFFFFF) {
                                  rlpNonce = new bytes(4);
                                  rlpNonce[0] = 0x83;
                                  rlpNonce[1] = byte(uint8((nonce & 0xFF0000) >> 16));
                                  rlpNonce[2] = byte(uint8((nonce & 0xFF00) >> 8));
                                  rlpNonce[3] = byte(uint8(nonce));
                              } else {
                                  rlpNonce = new bytes(5);
                                  rlpNonce[0] = 0x84;
                                  rlpNonce[1] = byte(uint8((nonce & 0xFF000000) >> 24));
                                  rlpNonce[2] = byte(uint8((nonce & 0xFF0000) >> 16));
                                  rlpNonce[3] = byte(uint8((nonce & 0xFF00) >> 8));
                                  rlpNonce[4] = byte(uint8(nonce));
                              }
                          }
                          /// @dev Compute the expected deployment address by `deployer` at
                          ///      the nonce given by `deploymentNonce`.
                          /// @param deployer The address of the deployer.
                          /// @param deploymentNonce The nonce that the deployer had when deploying
                          ///        a contract.
                          /// @return deploymentAddress The deployment address.
                          function getDeployedAddress(address deployer, uint32 deploymentNonce)
                              internal
                              pure
                              returns (address payable deploymentAddress)
                          {
                              // The address of if a deployed contract is the lower 20 bytes of the
                              // hash of the RLP-encoded deployer's account address + account nonce.
                              // See: https://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
                              bytes memory rlpNonce = rlpEncodeNonce(deploymentNonce);
                              return address(uint160(uint256(keccak256(abi.encodePacked(
                                  byte(uint8(0xC0 + 21 + rlpNonce.length)),
                                  byte(uint8(0x80 + 20)),
                                  deployer,
                                  rlpNonce
                              )))));
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../ZeroEx.sol";
                      import "../features/IOwnable.sol";
                      import "../features/TokenSpender.sol";
                      import "../features/TransformERC20.sol";
                      import "../features/SignatureValidator.sol";
                      import "../features/MetaTransactions.sol";
                      import "../external/AllowanceTarget.sol";
                      import "./InitialMigration.sol";
                      /// @dev A contract for deploying and configuring the full ZeroEx contract.
                      contract FullMigration {
                          // solhint-disable no-empty-blocks,indent
                          /// @dev Features to add the the proxy contract.
                          struct Features {
                              SimpleFunctionRegistry registry;
                              Ownable ownable;
                              TokenSpender tokenSpender;
                              TransformERC20 transformERC20;
                              SignatureValidator signatureValidator;
                              MetaTransactions metaTransactions;
                          }
                          /// @dev Parameters needed to initialize features.
                          struct MigrateOpts {
                              address transformerDeployer;
                          }
                          /// @dev The allowed caller of `deploy()`.
                          address public immutable deployer;
                          /// @dev The initial migration contract.
                          InitialMigration private _initialMigration;
                          /// @dev Instantiate this contract and set the allowed caller of `deploy()`
                          ///      to `deployer`.
                          /// @param deployer_ The allowed caller of `deploy()`.
                          constructor(address payable deployer_)
                              public
                          {
                              deployer = deployer_;
                              // Create an initial migration contract with this contract set to the
                              // allowed deployer.
                              _initialMigration = new InitialMigration(address(this));
                          }
                          /// @dev Retrieve the bootstrapper address to use when constructing `ZeroEx`.
                          /// @return bootstrapper The bootstrapper address.
                          function getBootstrapper()
                              external
                              view
                              returns (address bootstrapper)
                          {
                              return address(_initialMigration);
                          }
                          /// @dev Deploy the `ZeroEx` contract with the full feature set,
                          ///      transfer ownership to `owner`, then self-destruct.
                          /// @param owner The owner of the contract.
                          /// @param zeroEx The instance of the ZeroEx contract. ZeroEx should
                          ///        been constructed with this contract as the bootstrapper.
                          /// @param features Features to add to the proxy.
                          /// @return _zeroEx The configured ZeroEx contract. Same as the `zeroEx` parameter.
                          /// @param migrateOpts Parameters needed to initialize features.
                          function deploy(
                              address payable owner,
                              ZeroEx zeroEx,
                              Features memory features,
                              MigrateOpts memory migrateOpts
                          )
                              public
                              returns (ZeroEx _zeroEx)
                          {
                              require(msg.sender == deployer, "FullMigration/INVALID_SENDER");
                              // Perform the initial migration with the owner set to this contract.
                              _initialMigration.deploy(
                                  address(uint160(address(this))),
                                  zeroEx,
                                  InitialMigration.BootstrapFeatures({
                                      registry: features.registry,
                                      ownable: features.ownable
                                  })
                              );
                              // Add features.
                              _addFeatures(zeroEx, owner, features, migrateOpts);
                              // Transfer ownership to the real owner.
                              IOwnable(address(zeroEx)).transferOwnership(owner);
                              // Self-destruct.
                              this.die(owner);
                              return zeroEx;
                          }
                          /// @dev Destroy this contract. Only callable from ourselves (from `deploy()`).
                          /// @param ethRecipient Receiver of any ETH in this contract.
                          function die(address payable ethRecipient)
                              external
                              virtual
                          {
                              require(msg.sender == address(this), "FullMigration/INVALID_SENDER");
                              // This contract should not hold any funds but we send
                              // them to the ethRecipient just in case.
                              selfdestruct(ethRecipient);
                          }
                          /// @dev Deploy and register features to the ZeroEx contract.
                          /// @param zeroEx The bootstrapped ZeroEx contract.
                          /// @param owner The ultimate owner of the ZeroEx contract.
                          /// @param features Features to add to the proxy.
                          /// @param migrateOpts Parameters needed to initialize features.
                          function _addFeatures(
                              ZeroEx zeroEx,
                              address owner,
                              Features memory features,
                              MigrateOpts memory migrateOpts
                          )
                              private
                          {
                              IOwnable ownable = IOwnable(address(zeroEx));
                              // TokenSpender
                              {
                                  // Create the allowance target.
                                  AllowanceTarget allowanceTarget = new AllowanceTarget();
                                  // Let the ZeroEx contract use the allowance target.
                                  allowanceTarget.addAuthorizedAddress(address(zeroEx));
                                  // Transfer ownership of the allowance target to the (real) owner.
                                  allowanceTarget.transferOwnership(owner);
                                  // Register the feature.
                                  ownable.migrate(
                                      address(features.tokenSpender),
                                      abi.encodeWithSelector(
                                          TokenSpender.migrate.selector,
                                          allowanceTarget
                                      ),
                                      address(this)
                                  );
                              }
                              // TransformERC20
                              {
                                  // Register the feature.
                                  ownable.migrate(
                                      address(features.transformERC20),
                                      abi.encodeWithSelector(
                                          TransformERC20.migrate.selector,
                                          migrateOpts.transformerDeployer
                                      ),
                                      address(this)
                                  );
                              }
                              // SignatureValidator
                              {
                                  // Register the feature.
                                  ownable.migrate(
                                      address(features.signatureValidator),
                                      abi.encodeWithSelector(
                                          SignatureValidator.migrate.selector
                                      ),
                                      address(this)
                                  );
                              }
                              // MetaTransactions
                              {
                                  // Register the feature.
                                  ownable.migrate(
                                      address(features.metaTransactions),
                                      abi.encodeWithSelector(
                                          MetaTransactions.migrate.selector
                                      ),
                                      address(this)
                                  );
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../ZeroEx.sol";
                      import "../features/IBootstrap.sol";
                      import "../features/SimpleFunctionRegistry.sol";
                      import "../features/Ownable.sol";
                      import "./LibBootstrap.sol";
                      /// @dev A contract for deploying and configuring a minimal ZeroEx contract.
                      contract InitialMigration {
                          /// @dev Features to bootstrap into the the proxy contract.
                          struct BootstrapFeatures {
                              SimpleFunctionRegistry registry;
                              Ownable ownable;
                          }
                          /// @dev The allowed caller of `deploy()`. In production, this would be
                          ///      the governor.
                          address public immutable deployer;
                          /// @dev The real address of this contract.
                          address private immutable _implementation;
                          /// @dev Instantiate this contract and set the allowed caller of `deploy()`
                          ///      to `deployer_`.
                          /// @param deployer_ The allowed caller of `deploy()`.
                          constructor(address deployer_) public {
                              deployer = deployer_;
                              _implementation = address(this);
                          }
                          /// @dev Deploy the `ZeroEx` contract with the minimum feature set,
                          ///      transfers ownership to `owner`, then self-destructs.
                          ///      Only callable by `deployer` set in the contstructor.
                          /// @param owner The owner of the contract.
                          /// @param zeroEx The instance of the ZeroEx contract. ZeroEx should
                          ///        been constructed with this contract as the bootstrapper.
                          /// @param features Features to bootstrap into the proxy.
                          /// @return _zeroEx The configured ZeroEx contract. Same as the `zeroEx` parameter.
                          function deploy(
                              address payable owner,
                              ZeroEx zeroEx,
                              BootstrapFeatures memory features
                          )
                              public
                              virtual
                              returns (ZeroEx _zeroEx)
                          {
                              // Must be called by the allowed deployer.
                              require(msg.sender == deployer, "InitialMigration/INVALID_SENDER");
                              // Bootstrap the initial feature set.
                              IBootstrap(address(zeroEx)).bootstrap(
                                  address(this),
                                  abi.encodeWithSelector(this.bootstrap.selector, owner, features)
                              );
                              // Self-destruct. This contract should not hold any funds but we send
                              // them to the owner just in case.
                              this.die(owner);
                              return zeroEx;
                          }
                          /// @dev Sets up the initial state of the `ZeroEx` contract.
                          ///      The `ZeroEx` contract will delegatecall into this function.
                          /// @param owner The new owner of the ZeroEx contract.
                          /// @param features Features to bootstrap into the proxy.
                          /// @return success Magic bytes if successful.
                          function bootstrap(address owner, BootstrapFeatures memory features)
                              public
                              virtual
                              returns (bytes4 success)
                          {
                              // Deploy and migrate the initial features.
                              // Order matters here.
                              // Initialize Registry.
                              LibBootstrap.delegatecallBootstrapFunction(
                                  address(features.registry),
                                  abi.encodeWithSelector(
                                      SimpleFunctionRegistry.bootstrap.selector
                                  )
                              );
                              // Initialize Ownable.
                              LibBootstrap.delegatecallBootstrapFunction(
                                  address(features.ownable),
                                  abi.encodeWithSelector(
                                      Ownable.bootstrap.selector
                                  )
                              );
                              // De-register `SimpleFunctionRegistry._extendSelf`.
                              SimpleFunctionRegistry(address(this)).rollback(
                                  SimpleFunctionRegistry._extendSelf.selector,
                                  address(0)
                              );
                              // Transfer ownership to the real owner.
                              Ownable(address(this)).transferOwnership(owner);
                              success = LibBootstrap.BOOTSTRAP_SUCCESS;
                          }
                          /// @dev Self-destructs this contract. Only callable by this contract.
                          /// @param ethRecipient Who to transfer outstanding ETH to.
                          function die(address payable ethRecipient) public virtual {
                              require(msg.sender == _implementation, "InitialMigration/INVALID_SENDER");
                              selfdestruct(ethRecipient);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
                      import "../errors/LibTransformERC20RichErrors.sol";
                      import "./Transformer.sol";
                      import "./LibERC20Transformer.sol";
                      /// @dev A transformer that transfers tokens to arbitrary addresses.
                      contract AffiliateFeeTransformer is
                          Transformer
                      {
                          // solhint-disable no-empty-blocks
                          using LibRichErrorsV06 for bytes;
                          using LibSafeMathV06 for uint256;
                          using LibERC20Transformer for IERC20TokenV06;
                          /// @dev Information for a single fee.
                          struct TokenFee {
                              // The token to transfer to `recipient`.
                              IERC20TokenV06 token;
                              // Amount of each `token` to transfer to `recipient`.
                              // If `amount == uint256(-1)`, the entire balance of `token` will be
                              // transferred.
                              uint256 amount;
                              // Recipient of `token`.
                              address payable recipient;
                          }
                          uint256 private constant MAX_UINT256 = uint256(-1);
                          /// @dev Create this contract.
                          constructor()
                              public
                              Transformer()
                          {}
                          /// @dev Transfers tokens to recipients.
                          /// @param data ABI-encoded `TokenFee[]`, indicating which tokens to transfer.
                          /// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
                          function transform(
                              bytes32, // callDataHash,
                              address payable, // taker,
                              bytes calldata data
                          )
                              external
                              override
                              returns (bytes4 success)
                          {
                              TokenFee[] memory fees = abi.decode(data, (TokenFee[]));
                              // Transfer tokens to recipients.
                              for (uint256 i = 0; i < fees.length; ++i) {
                                  uint256 amount = fees[i].amount;
                                  if (amount == MAX_UINT256) {
                                      amount = LibERC20Transformer.getTokenBalanceOf(fees[i].token, address(this));
                                  }
                                  if (amount != 0) {
                                      fees[i].token.transformerTransfer(fees[i].recipient, amount);
                                  }
                              }
                              return LibERC20Transformer.TRANSFORMER_SUCCESS;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../errors/LibTransformERC20RichErrors.sol";
                      import "./IERC20Transformer.sol";
                      /// @dev Abstract base class for transformers.
                      abstract contract Transformer is
                          IERC20Transformer
                      {
                          using LibRichErrorsV06 for bytes;
                          /// @dev The address of the deployer.
                          address public immutable deployer;
                          /// @dev The original address of this contract.
                          address private immutable _implementation;
                          /// @dev Create this contract.
                          constructor() public {
                              deployer = msg.sender;
                              _implementation = address(this);
                          }
                          /// @dev Destruct this contract. Only callable by the deployer and will not
                          ///      succeed in the context of a delegatecall (from another contract).
                          /// @param ethRecipient The recipient of ETH held in this contract.
                          function die(address payable ethRecipient)
                              external
                              virtual
                          {
                              // Only the deployer can call this.
                              if (msg.sender != deployer) {
                                  LibTransformERC20RichErrors
                                      .OnlyCallableByDeployerError(msg.sender, deployer)
                                      .rrevert();
                              }
                              // Must be executing our own context.
                              if (address(this) != _implementation) {
                                  LibTransformERC20RichErrors
                                      .InvalidExecutionContextError(address(this), _implementation)
                                      .rrevert();
                              }
                              selfdestruct(ethRecipient);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
                      import "../errors/LibTransformERC20RichErrors.sol";
                      import "../vendor/v3/IExchange.sol";
                      import "./Transformer.sol";
                      import "./LibERC20Transformer.sol";
                      /// @dev A transformer that fills an ERC20 market sell/buy quote.
                      contract FillQuoteTransformer is
                          Transformer
                      {
                          using LibERC20TokenV06 for IERC20TokenV06;
                          using LibERC20Transformer for IERC20TokenV06;
                          using LibSafeMathV06 for uint256;
                          using LibRichErrorsV06 for bytes;
                          /// @dev Whether we are performing a market sell or buy.
                          enum Side {
                              Sell,
                              Buy
                          }
                          /// @dev Transform data to ABI-encode and pass into `transform()`.
                          struct TransformData {
                              // Whether we aer performing a market sell or buy.
                              Side side;
                              // The token being sold.
                              // This should be an actual token, not the ETH pseudo-token.
                              IERC20TokenV06 sellToken;
                              // The token being bought.
                              // This should be an actual token, not the ETH pseudo-token.
                              IERC20TokenV06 buyToken;
                              // The orders to fill.
                              IExchange.Order[] orders;
                              // Signatures for each respective order in `orders`.
                              bytes[] signatures;
                              // Maximum fill amount for each order. This may be shorter than the
                              // number of orders, where missing entries will be treated as `uint256(-1)`.
                              // For sells, this will be the maximum sell amount (taker asset).
                              // For buys, this will be the maximum buy amount (maker asset).
                              uint256[] maxOrderFillAmounts;
                              // Amount of `sellToken` to sell or `buyToken` to buy.
                              // For sells, this may be `uint256(-1)` to sell the entire balance of
                              // `sellToken`.
                              uint256 fillAmount;
                          }
                          /// @dev Results of a call to `_fillOrder()`.
                          struct FillOrderResults {
                              // The amount of taker tokens sold, according to balance checks.
                              uint256 takerTokenSoldAmount;
                              // The amount of maker tokens sold, according to balance checks.
                              uint256 makerTokenBoughtAmount;
                              // The amount of protocol fee paid.
                              uint256 protocolFeePaid;
                          }
                          /// @dev The Exchange ERC20Proxy ID.
                          bytes4 private constant ERC20_ASSET_PROXY_ID = 0xf47261b0;
                          /// @dev Maximum uint256 value.
                          uint256 private constant MAX_UINT256 = uint256(-1);
                          /// @dev The Exchange contract.
                          IExchange public immutable exchange;
                          /// @dev The ERC20Proxy address.
                          address public immutable erc20Proxy;
                          /// @dev Create this contract.
                          /// @param exchange_ The Exchange V3 instance.
                          constructor(IExchange exchange_)
                              public
                              Transformer()
                          {
                              exchange = exchange_;
                              erc20Proxy = exchange_.getAssetProxy(ERC20_ASSET_PROXY_ID);
                          }
                          /// @dev Sell this contract's entire balance of of `sellToken` in exchange
                          ///      for `buyToken` by filling `orders`. Protocol fees should be attached
                          ///      to this call. `buyToken` and excess ETH will be transferred back to the caller.
                          /// @param data_ ABI-encoded `TransformData`.
                          /// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
                          function transform(
                              bytes32, // callDataHash,
                              address payable, // taker,
                              bytes calldata data_
                          )
                              external
                              override
                              returns (bytes4 success)
                          {
                              TransformData memory data = abi.decode(data_, (TransformData));
                              // Validate data fields.
                              if (data.sellToken.isTokenETH() || data.buyToken.isTokenETH()) {
                                  LibTransformERC20RichErrors.InvalidTransformDataError(
                                      LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_TOKENS,
                                      data_
                                  ).rrevert();
                              }
                              if (data.orders.length != data.signatures.length) {
                                  LibTransformERC20RichErrors.InvalidTransformDataError(
                                      LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_ARRAY_LENGTH,
                                      data_
                                  ).rrevert();
                              }
                              if (data.side == Side.Sell && data.fillAmount == MAX_UINT256) {
                                  // If `sellAmount == -1 then we are selling
                                  // the entire balance of `sellToken`. This is useful in cases where
                                  // the exact sell amount is not exactly known in advance, like when
                                  // unwrapping Chai/cUSDC/cDAI.
                                  data.fillAmount = data.sellToken.getTokenBalanceOf(address(this));
                              }
                              // Approve the ERC20 proxy to spend `sellToken`.
                              data.sellToken.approveIfBelow(erc20Proxy, data.fillAmount);
                              // Fill the orders.
                              uint256 singleProtocolFee = exchange.protocolFeeMultiplier().safeMul(tx.gasprice);
                              uint256 ethRemaining = address(this).balance;
                              uint256 boughtAmount = 0;
                              uint256 soldAmount = 0;
                              for (uint256 i = 0; i < data.orders.length; ++i) {
                                  // Check if we've hit our targets.
                                  if (data.side == Side.Sell) {
                                      // Market sell check.
                                      if (soldAmount >= data.fillAmount) {
                                          break;
                                      }
                                  } else {
                                      // Market buy check.
                                      if (boughtAmount >= data.fillAmount) {
                                          break;
                                      }
                                  }
                                  // Ensure we have enough ETH to cover the protocol fee.
                                  if (ethRemaining < singleProtocolFee) {
                                      LibTransformERC20RichErrors
                                          .InsufficientProtocolFeeError(ethRemaining, singleProtocolFee)
                                          .rrevert();
                                  }
                                  // Fill the order.
                                  FillOrderResults memory results;
                                  if (data.side == Side.Sell) {
                                      // Market sell.
                                      results = _sellToOrder(
                                          data.buyToken,
                                          data.sellToken,
                                          data.orders[i],
                                          data.signatures[i],
                                          data.fillAmount.safeSub(soldAmount).min256(
                                              data.maxOrderFillAmounts.length > i
                                              ? data.maxOrderFillAmounts[i]
                                              : MAX_UINT256
                                          ),
                                          singleProtocolFee
                                      );
                                  } else {
                                      // Market buy.
                                      results = _buyFromOrder(
                                          data.buyToken,
                                          data.sellToken,
                                          data.orders[i],
                                          data.signatures[i],
                                          data.fillAmount.safeSub(boughtAmount).min256(
                                              data.maxOrderFillAmounts.length > i
                                              ? data.maxOrderFillAmounts[i]
                                              : MAX_UINT256
                                          ),
                                          singleProtocolFee
                                      );
                                  }
                                  // Accumulate totals.
                                  soldAmount = soldAmount.safeAdd(results.takerTokenSoldAmount);
                                  boughtAmount = boughtAmount.safeAdd(results.makerTokenBoughtAmount);
                                  ethRemaining = ethRemaining.safeSub(results.protocolFeePaid);
                              }
                              // Ensure we hit our targets.
                              if (data.side == Side.Sell) {
                                  // Market sell check.
                                  if (soldAmount < data.fillAmount) {
                                      LibTransformERC20RichErrors
                                          .IncompleteFillSellQuoteError(
                                              address(data.sellToken),
                                              soldAmount,
                                              data.fillAmount
                                          ).rrevert();
                                  }
                              } else {
                                  // Market buy check.
                                  if (boughtAmount < data.fillAmount) {
                                      LibTransformERC20RichErrors
                                          .IncompleteFillBuyQuoteError(
                                              address(data.buyToken),
                                              boughtAmount,
                                              data.fillAmount
                                          ).rrevert();
                                  }
                              }
                              return LibERC20Transformer.TRANSFORMER_SUCCESS;
                          }
                          /// @dev Try to sell up to `sellAmount` from an order.
                          /// @param makerToken The maker/buy token.
                          /// @param takerToken The taker/sell token.
                          /// @param order The order to fill.
                          /// @param signature The signature for `order`.
                          /// @param sellAmount Amount of taker token to sell.
                          /// @param protocolFee The protocol fee needed to fill `order`.
                          function _sellToOrder(
                              IERC20TokenV06 makerToken,
                              IERC20TokenV06 takerToken,
                              IExchange.Order memory order,
                              bytes memory signature,
                              uint256 sellAmount,
                              uint256 protocolFee
                          )
                              private
                              returns (FillOrderResults memory results)
                          {
                              IERC20TokenV06 takerFeeToken =
                                  _getTokenFromERC20AssetData(order.takerFeeAssetData);
                              uint256 takerTokenFillAmount = sellAmount;
                              if (order.takerFee != 0) {
                                  if (takerFeeToken == makerToken) {
                                      // Taker fee is payable in the maker token, so we need to
                                      // approve the proxy to spend the maker token.
                                      // It isn't worth computing the actual taker fee
                                      // since `approveIfBelow()` will set the allowance to infinite. We
                                      // just need a reasonable upper bound to avoid unnecessarily re-approving.
                                      takerFeeToken.approveIfBelow(erc20Proxy, order.takerFee);
                                  } else if (takerFeeToken == takerToken){
                                      // Taker fee is payable in the taker token, so we need to
                                      // reduce the fill amount to cover the fee.
                                      // takerTokenFillAmount' =
                                      //   (takerTokenFillAmount * order.takerAssetAmount) /
                                      //   (order.takerAssetAmount + order.takerFee)
                                      takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
                                          order.takerAssetAmount,
                                          order.takerAssetAmount.safeAdd(order.takerFee),
                                          sellAmount
                                      );
                                  } else {
                                      //  Only support taker or maker asset denominated taker fees.
                                      LibTransformERC20RichErrors.InvalidTakerFeeTokenError(
                                          address(takerFeeToken)
                                      ).rrevert();
                                  }
                              }
                              // Clamp fill amount to order size.
                              takerTokenFillAmount = LibSafeMathV06.min256(
                                  takerTokenFillAmount,
                                  order.takerAssetAmount
                              );
                              // Perform the fill.
                              return _fillOrder(
                                  order,
                                  signature,
                                  takerTokenFillAmount,
                                  protocolFee,
                                  makerToken,
                                  takerFeeToken == takerToken
                              );
                          }
                          /// @dev Try to buy up to `buyAmount` from an order.
                          /// @param makerToken The maker/buy token.
                          /// @param takerToken The taker/sell token.
                          /// @param order The order to fill.
                          /// @param signature The signature for `order`.
                          /// @param buyAmount Amount of maker token to buy.
                          /// @param protocolFee The protocol fee needed to fill `order`.
                          function _buyFromOrder(
                              IERC20TokenV06 makerToken,
                              IERC20TokenV06 takerToken,
                              IExchange.Order memory order,
                              bytes memory signature,
                              uint256 buyAmount,
                              uint256 protocolFee
                          )
                              private
                              returns (FillOrderResults memory results)
                          {
                              IERC20TokenV06 takerFeeToken =
                                  _getTokenFromERC20AssetData(order.takerFeeAssetData);
                              // Compute the default taker token fill amount.
                              uint256 takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
                                  buyAmount,
                                  order.makerAssetAmount,
                                  order.takerAssetAmount
                              );
                              if (order.takerFee != 0) {
                                  if (takerFeeToken == makerToken) {
                                      // Taker fee is payable in the maker token.
                                      // Adjust the taker token fill amount to account for maker
                                      // tokens being lost to the taker fee.
                                      // takerTokenFillAmount' =
                                      //  (order.takerAssetAmount * buyAmount) /
                                      //  (order.makerAssetAmount - order.takerFee)
                                      takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
                                          buyAmount,
                                          order.makerAssetAmount.safeSub(order.takerFee),
                                          order.takerAssetAmount
                                      );
                                      // Approve the proxy to spend the maker token.
                                      // It isn't worth computing the actual taker fee
                                      // since `approveIfBelow()` will set the allowance to infinite. We
                                      // just need a reasonable upper bound to avoid unnecessarily re-approving.
                                      takerFeeToken.approveIfBelow(erc20Proxy, order.takerFee);
                                  } else if (takerFeeToken != takerToken) {
                                      //  Only support taker or maker asset denominated taker fees.
                                      LibTransformERC20RichErrors.InvalidTakerFeeTokenError(
                                          address(takerFeeToken)
                                      ).rrevert();
                                  }
                              }
                              // Clamp to order size.
                              takerTokenFillAmount = LibSafeMathV06.min256(
                                  order.takerAssetAmount,
                                  takerTokenFillAmount
                              );
                              // Perform the fill.
                              return _fillOrder(
                                  order,
                                  signature,
                                  takerTokenFillAmount,
                                  protocolFee,
                                  makerToken,
                                  takerFeeToken == takerToken
                              );
                          }
                          /// @dev Attempt to fill an order. If the fill reverts, the revert will be
                          ///      swallowed and `results` will be zeroed out.
                          /// @param order The order to fill.
                          /// @param signature The order signature.
                          /// @param takerAssetFillAmount How much taker asset to fill.
                          /// @param protocolFee The protocol fee needed to fill this order.
                          /// @param makerToken The maker token.
                          /// @param isTakerFeeInTakerToken Whether the taker fee token is the same as the
                          ///        taker token.
                          function _fillOrder(
                              IExchange.Order memory order,
                              bytes memory signature,
                              uint256 takerAssetFillAmount,
                              uint256 protocolFee,
                              IERC20TokenV06 makerToken,
                              bool isTakerFeeInTakerToken
                          )
                              private
                              returns (FillOrderResults memory results)
                          {
                              // Track changes in the maker token balance.
                              uint256 initialMakerTokenBalance = makerToken.balanceOf(address(this));
                              try
                                  exchange.fillOrder
                                      {value: protocolFee}
                                      (order, takerAssetFillAmount, signature)
                                  returns (IExchange.FillResults memory fillResults)
                              {
                                  // Update maker quantity based on changes in token balances.
                                  results.makerTokenBoughtAmount = makerToken.balanceOf(address(this))
                                      .safeSub(initialMakerTokenBalance);
                                  // We can trust the other fill result quantities.
                                  results.protocolFeePaid = fillResults.protocolFeePaid;
                                  results.takerTokenSoldAmount = fillResults.takerAssetFilledAmount;
                                  // If the taker fee is payable in the taker asset, include the
                                  // taker fee in the total amount sold.
                                  if (isTakerFeeInTakerToken) {
                                      results.takerTokenSoldAmount =
                                          results.takerTokenSoldAmount.safeAdd(fillResults.takerFeePaid);
                                  }
                              } catch (bytes memory) {
                                  // Swallow failures, leaving all results as zero.
                              }
                          }
                          /// @dev Extract the token from plain ERC20 asset data.
                          ///      If the asset-data is empty, a zero token address will be returned.
                          /// @param assetData The order asset data.
                          function _getTokenFromERC20AssetData(bytes memory assetData)
                              private
                              pure
                              returns (IERC20TokenV06 token)
                          {
                              if (assetData.length == 0) {
                                  return IERC20TokenV06(address(0));
                              }
                              if (assetData.length != 36 ||
                                  LibBytesV06.readBytes4(assetData, 0) != ERC20_ASSET_PROXY_ID)
                              {
                                  LibTransformERC20RichErrors
                                      .InvalidERC20AssetDataError(assetData)
                                      .rrevert();
                              }
                              return IERC20TokenV06(LibBytesV06.readAddress(assetData, 16));
                          }
                      }
                      /*
                        Copyright 2019 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./LibSafeMathV06.sol";
                      import "./errors/LibRichErrorsV06.sol";
                      import "./errors/LibMathRichErrorsV06.sol";
                      library LibMathV06 {
                          using LibSafeMathV06 for uint256;
                          /// @dev Calculates partial value given a numerator and denominator rounded down.
                          ///      Reverts if rounding error is >= 0.1%
                          /// @param numerator Numerator.
                          /// @param denominator Denominator.
                          /// @param target Value to calculate partial of.
                          /// @return partialAmount Partial value of target rounded down.
                          function safeGetPartialAmountFloor(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (uint256 partialAmount)
                          {
                              if (isRoundingErrorFloor(
                                      numerator,
                                      denominator,
                                      target
                              )) {
                                  LibRichErrorsV06.rrevert(LibMathRichErrorsV06.RoundingError(
                                      numerator,
                                      denominator,
                                      target
                                  ));
                              }
                              partialAmount = numerator.safeMul(target).safeDiv(denominator);
                              return partialAmount;
                          }
                          /// @dev Calculates partial value given a numerator and denominator rounded down.
                          ///      Reverts if rounding error is >= 0.1%
                          /// @param numerator Numerator.
                          /// @param denominator Denominator.
                          /// @param target Value to calculate partial of.
                          /// @return partialAmount Partial value of target rounded up.
                          function safeGetPartialAmountCeil(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (uint256 partialAmount)
                          {
                              if (isRoundingErrorCeil(
                                      numerator,
                                      denominator,
                                      target
                              )) {
                                  LibRichErrorsV06.rrevert(LibMathRichErrorsV06.RoundingError(
                                      numerator,
                                      denominator,
                                      target
                                  ));
                              }
                              // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
                              //       ceil(a / b) = floor((a + b - 1) / b)
                              // To implement `ceil(a / b)` using safeDiv.
                              partialAmount = numerator.safeMul(target)
                                  .safeAdd(denominator.safeSub(1))
                                  .safeDiv(denominator);
                              return partialAmount;
                          }
                          /// @dev Calculates partial value given a numerator and denominator rounded down.
                          /// @param numerator Numerator.
                          /// @param denominator Denominator.
                          /// @param target Value to calculate partial of.
                          /// @return partialAmount Partial value of target rounded down.
                          function getPartialAmountFloor(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (uint256 partialAmount)
                          {
                              partialAmount = numerator.safeMul(target).safeDiv(denominator);
                              return partialAmount;
                          }
                          /// @dev Calculates partial value given a numerator and denominator rounded down.
                          /// @param numerator Numerator.
                          /// @param denominator Denominator.
                          /// @param target Value to calculate partial of.
                          /// @return partialAmount Partial value of target rounded up.
                          function getPartialAmountCeil(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (uint256 partialAmount)
                          {
                              // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
                              //       ceil(a / b) = floor((a + b - 1) / b)
                              // To implement `ceil(a / b)` using safeDiv.
                              partialAmount = numerator.safeMul(target)
                                  .safeAdd(denominator.safeSub(1))
                                  .safeDiv(denominator);
                              return partialAmount;
                          }
                          /// @dev Checks if rounding error >= 0.1% when rounding down.
                          /// @param numerator Numerator.
                          /// @param denominator Denominator.
                          /// @param target Value to multiply with numerator/denominator.
                          /// @return isError Rounding error is present.
                          function isRoundingErrorFloor(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (bool isError)
                          {
                              if (denominator == 0) {
                                  LibRichErrorsV06.rrevert(LibMathRichErrorsV06.DivisionByZeroError());
                              }
                              // The absolute rounding error is the difference between the rounded
                              // value and the ideal value. The relative rounding error is the
                              // absolute rounding error divided by the absolute value of the
                              // ideal value. This is undefined when the ideal value is zero.
                              //
                              // The ideal value is `numerator * target / denominator`.
                              // Let's call `numerator * target % denominator` the remainder.
                              // The absolute error is `remainder / denominator`.
                              //
                              // When the ideal value is zero, we require the absolute error to
                              // be zero. Fortunately, this is always the case. The ideal value is
                              // zero iff `numerator == 0` and/or `target == 0`. In this case the
                              // remainder and absolute error are also zero.
                              if (target == 0 || numerator == 0) {
                                  return false;
                              }
                              // Otherwise, we want the relative rounding error to be strictly
                              // less than 0.1%.
                              // The relative error is `remainder / (numerator * target)`.
                              // We want the relative error less than 1 / 1000:
                              //        remainder / (numerator * denominator)  <  1 / 1000
                              // or equivalently:
                              //        1000 * remainder  <  numerator * target
                              // so we have a rounding error iff:
                              //        1000 * remainder  >=  numerator * target
                              uint256 remainder = mulmod(
                                  target,
                                  numerator,
                                  denominator
                              );
                              isError = remainder.safeMul(1000) >= numerator.safeMul(target);
                              return isError;
                          }
                          /// @dev Checks if rounding error >= 0.1% when rounding up.
                          /// @param numerator Numerator.
                          /// @param denominator Denominator.
                          /// @param target Value to multiply with numerator/denominator.
                          /// @return isError Rounding error is present.
                          function isRoundingErrorCeil(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (bool isError)
                          {
                              if (denominator == 0) {
                                  LibRichErrorsV06.rrevert(LibMathRichErrorsV06.DivisionByZeroError());
                              }
                              // See the comments in `isRoundingError`.
                              if (target == 0 || numerator == 0) {
                                  // When either is zero, the ideal value and rounded value are zero
                                  // and there is no rounding error. (Although the relative error
                                  // is undefined.)
                                  return false;
                              }
                              // Compute remainder as before
                              uint256 remainder = mulmod(
                                  target,
                                  numerator,
                                  denominator
                              );
                              remainder = denominator.safeSub(remainder) % denominator;
                              isError = remainder.safeMul(1000) >= numerator.safeMul(target);
                              return isError;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibMathRichErrorsV06 {
                          // bytes4(keccak256("DivisionByZeroError()"))
                          bytes internal constant DIVISION_BY_ZERO_ERROR =
                              hex"a791837c";
                          // bytes4(keccak256("RoundingError(uint256,uint256,uint256)"))
                          bytes4 internal constant ROUNDING_ERROR_SELECTOR =
                              0x339f3de2;
                          // solhint-disable func-name-mixedcase
                          function DivisionByZeroError()
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return DIVISION_BY_ZERO_ERROR;
                          }
                          function RoundingError(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  ROUNDING_ERROR_SELECTOR,
                                  numerator,
                                  denominator,
                                  target
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      /// @dev Interface to the V3 Exchange.
                      interface IExchange {
                          /// @dev V3 Order structure.
                          struct Order {
                              // Address that created the order.
                              address makerAddress;
                              // Address that is allowed to fill the order.
                              // If set to 0, any address is allowed to fill the order.
                              address takerAddress;
                              // Address that will recieve fees when order is filled.
                              address feeRecipientAddress;
                              // Address that is allowed to call Exchange contract methods that affect this order.
                              // If set to 0, any address is allowed to call these methods.
                              address senderAddress;
                              // Amount of makerAsset being offered by maker. Must be greater than 0.
                              uint256 makerAssetAmount;
                              // Amount of takerAsset being bid on by maker. Must be greater than 0.
                              uint256 takerAssetAmount;
                              // Fee paid to feeRecipient by maker when order is filled.
                              uint256 makerFee;
                              // Fee paid to feeRecipient by taker when order is filled.
                              uint256 takerFee;
                              // Timestamp in seconds at which order expires.
                              uint256 expirationTimeSeconds;
                              // Arbitrary number to facilitate uniqueness of the order's hash.
                              uint256 salt;
                              // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset.
                              // The leading bytes4 references the id of the asset proxy.
                              bytes makerAssetData;
                              // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset.
                              // The leading bytes4 references the id of the asset proxy.
                              bytes takerAssetData;
                              // Encoded data that can be decoded by a specified proxy contract when transferring makerFeeAsset.
                              // The leading bytes4 references the id of the asset proxy.
                              bytes makerFeeAssetData;
                              // Encoded data that can be decoded by a specified proxy contract when transferring takerFeeAsset.
                              // The leading bytes4 references the id of the asset proxy.
                              bytes takerFeeAssetData;
                          }
                          /// @dev V3 `fillOrder()` results.`
                          struct FillResults {
                              // Total amount of makerAsset(s) filled.
                              uint256 makerAssetFilledAmount;
                              // Total amount of takerAsset(s) filled.
                              uint256 takerAssetFilledAmount;
                              // Total amount of fees paid by maker(s) to feeRecipient(s).
                              uint256 makerFeePaid;
                              // Total amount of fees paid by taker to feeRecipients(s).
                              uint256 takerFeePaid;
                              // Total amount of fees paid by taker to the staking contract.
                              uint256 protocolFeePaid;
                          }
                          /// @dev Fills the input order.
                          /// @param order Order struct containing order specifications.
                          /// @param takerAssetFillAmount Desired amount of takerAsset to sell.
                          /// @param signature Proof that order has been created by maker.
                          /// @return fillResults Amounts filled and fees paid by maker and taker.
                          function fillOrder(
                              Order calldata order,
                              uint256 takerAssetFillAmount,
                              bytes calldata signature
                          )
                              external
                              payable
                              returns (FillResults memory fillResults);
                          /// @dev Returns the protocolFeeMultiplier
                          /// @return multiplier The multiplier for protocol fees.
                          function protocolFeeMultiplier()
                              external
                              view
                              returns (uint256 multiplier);
                          /// @dev Gets an asset proxy.
                          /// @param assetProxyId Id of the asset proxy.
                          /// @return proxyAddress The asset proxy registered to assetProxyId.
                          ///         Returns 0x0 if no proxy is registered.
                          function getAssetProxy(bytes4 assetProxyId)
                              external
                              view
                              returns (address proxyAddress);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
                      import "../errors/LibTransformERC20RichErrors.sol";
                      import "./Transformer.sol";
                      import "./LibERC20Transformer.sol";
                      /// @dev A transformer that transfers tokens to the taker.
                      contract PayTakerTransformer is
                          Transformer
                      {
                          // solhint-disable no-empty-blocks
                          using LibRichErrorsV06 for bytes;
                          using LibSafeMathV06 for uint256;
                          using LibERC20Transformer for IERC20TokenV06;
                          /// @dev Transform data to ABI-encode and pass into `transform()`.
                          struct TransformData {
                              // The tokens to transfer to the taker.
                              IERC20TokenV06[] tokens;
                              // Amount of each token in `tokens` to transfer to the taker.
                              // `uint(-1)` will transfer the entire balance.
                              uint256[] amounts;
                          }
                          /// @dev Maximum uint256 value.
                          uint256 private constant MAX_UINT256 = uint256(-1);
                          /// @dev Create this contract.
                          constructor()
                              public
                              Transformer()
                          {}
                          /// @dev Forwards tokens to the taker.
                          /// @param taker The taker address (caller of `TransformERC20.transformERC20()`).
                          /// @param data_ ABI-encoded `TransformData`, indicating which tokens to transfer.
                          /// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
                          function transform(
                              bytes32, // callDataHash,
                              address payable taker,
                              bytes calldata data_
                          )
                              external
                              override
                              returns (bytes4 success)
                          {
                              TransformData memory data = abi.decode(data_, (TransformData));
                              // Transfer tokens directly to the taker.
                              for (uint256 i = 0; i < data.tokens.length; ++i) {
                                  // The `amounts` array can be shorter than the `tokens` array.
                                  // Missing elements are treated as `uint256(-1)`.
                                  uint256 amount = data.amounts.length > i ? data.amounts[i] : uint256(-1);
                                  if (amount == MAX_UINT256) {
                                      amount = data.tokens[i].getTokenBalanceOf(address(this));
                                  }
                                  if (amount != 0) {
                                      data.tokens[i].transformerTransfer(taker, amount);
                                  }
                              }
                              return LibERC20Transformer.TRANSFORMER_SUCCESS;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
                      import "../errors/LibTransformERC20RichErrors.sol";
                      import "./Transformer.sol";
                      import "./LibERC20Transformer.sol";
                      /// @dev A transformer that wraps or unwraps WETH.
                      contract WethTransformer is
                          Transformer
                      {
                          using LibRichErrorsV06 for bytes;
                          using LibSafeMathV06 for uint256;
                          using LibERC20Transformer for IERC20TokenV06;
                          /// @dev Transform data to ABI-encode and pass into `transform()`.
                          struct TransformData {
                              // The token to wrap/unwrap. Must be either ETH or WETH.
                              IERC20TokenV06 token;
                              // Amount of `token` to wrap or unwrap.
                              // `uint(-1)` will unwrap the entire balance.
                              uint256 amount;
                          }
                          /// @dev The WETH contract address.
                          IEtherTokenV06 public immutable weth;
                          /// @dev Maximum uint256 value.
                          uint256 private constant MAX_UINT256 = uint256(-1);
                          /// @dev Construct the transformer and store the WETH address in an immutable.
                          /// @param weth_ The weth token.
                          constructor(IEtherTokenV06 weth_)
                              public
                              Transformer()
                          {
                              weth = weth_;
                          }
                          /// @dev Wraps and unwraps WETH.
                          /// @param data_ ABI-encoded `TransformData`, indicating which token to wrap/umwrap.
                          /// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
                          function transform(
                              bytes32, // callDataHash,
                              address payable, // taker,
                              bytes calldata data_
                          )
                              external
                              override
                              returns (bytes4 success)
                          {
                              TransformData memory data = abi.decode(data_, (TransformData));
                              if (!data.token.isTokenETH() && data.token != weth) {
                                  LibTransformERC20RichErrors.InvalidTransformDataError(
                                      LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_TOKENS,
                                      data_
                                  ).rrevert();
                              }
                              uint256 amount = data.amount;
                              if (amount == MAX_UINT256) {
                                  amount = data.token.getTokenBalanceOf(address(this));
                              }
                              if (amount != 0) {
                                  if (data.token.isTokenETH()) {
                                      // Wrap ETH.
                                      weth.deposit{value: amount}();
                                  } else {
                                      // Unwrap WETH.
                                      weth.withdraw(amount);
                                  }
                              }
                              return LibERC20Transformer.TRANSFORMER_SUCCESS;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./IERC20TokenV06.sol";
                      interface IEtherTokenV06 is
                          IERC20TokenV06
                      {
                          /// @dev Wrap ether.
                          function deposit() external payable;
                          /// @dev Unwrap ether.
                          function withdraw(uint256 amount) external;
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      interface ITestSimpleFunctionRegistryFeature {
                          function testFn() external view returns (uint256 id);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      contract TestCallTarget {
                          event CallTargetCalled(
                              address context,
                              address sender,
                              bytes data,
                              uint256 value
                          );
                          bytes4 private constant MAGIC_BYTES = 0x12345678;
                          bytes private constant REVERTING_DATA = hex"1337";
                          fallback() external payable {
                              if (keccak256(msg.data) == keccak256(REVERTING_DATA)) {
                                  revert("TestCallTarget/REVERT");
                              }
                              emit CallTargetCalled(
                                  address(this),
                                  msg.sender,
                                  msg.data,
                                  msg.value
                              );
                              bytes4 rval = MAGIC_BYTES;
                              assembly {
                                  mstore(0, rval)
                                  return(0, 32)
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      contract TestDelegateCaller {
                          function executeDelegateCall(
                              address target,
                              bytes calldata callData
                          )
                              external
                          {
                              (bool success, bytes memory resultData) = target.delegatecall(callData);
                              if (!success) {
                                  assembly { revert(add(resultData, 32), mload(resultData)) }
                              }
                              assembly { return(add(resultData, 32), mload(resultData)) }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "../src/vendor/v3/IExchange.sol";
                      import "./TestMintableERC20Token.sol";
                      contract TestFillQuoteTransformerExchange {
                          struct FillBehavior {
                              // How much of the order is filled, in taker asset amount.
                              uint256 filledTakerAssetAmount;
                              // Scaling for maker assets minted, in 1e18.
                              uint256 makerAssetMintRatio;
                          }
                          uint256 private constant PROTOCOL_FEE_MULTIPLIER = 1337;
                          using LibSafeMathV06 for uint256;
                          function fillOrder(
                              IExchange.Order calldata order,
                              uint256 takerAssetFillAmount,
                              bytes calldata signature
                          )
                              external
                              payable
                              returns (IExchange.FillResults memory fillResults)
                          {
                              require(
                                  signature.length != 0,
                                  "TestFillQuoteTransformerExchange/INVALID_SIGNATURE"
                              );
                              // The signature is the ABI-encoded FillBehavior data.
                              FillBehavior memory behavior = abi.decode(signature, (FillBehavior));
                              uint256 protocolFee = PROTOCOL_FEE_MULTIPLIER * tx.gasprice;
                              require(
                                  msg.value == protocolFee,
                                  "TestFillQuoteTransformerExchange/INSUFFICIENT_PROTOCOL_FEE"
                              );
                              // Return excess protocol fee.
                              msg.sender.transfer(msg.value - protocolFee);
                              // Take taker tokens.
                              TestMintableERC20Token takerToken = _getTokenFromAssetData(order.takerAssetData);
                              takerAssetFillAmount = LibSafeMathV06.min256(
                                  order.takerAssetAmount.safeSub(behavior.filledTakerAssetAmount),
                                  takerAssetFillAmount
                              );
                              require(
                                  takerToken.getSpendableAmount(msg.sender, address(this)) >= takerAssetFillAmount,
                                  "TestFillQuoteTransformerExchange/INSUFFICIENT_TAKER_FUNDS"
                              );
                              takerToken.transferFrom(msg.sender, order.makerAddress, takerAssetFillAmount);
                              // Mint maker tokens.
                              uint256 makerAssetFilledAmount = LibMathV06.getPartialAmountFloor(
                                  takerAssetFillAmount,
                                  order.takerAssetAmount,
                                  order.makerAssetAmount
                              );
                              TestMintableERC20Token makerToken = _getTokenFromAssetData(order.makerAssetData);
                              makerToken.mint(
                                  msg.sender,
                                  LibMathV06.getPartialAmountFloor(
                                      behavior.makerAssetMintRatio,
                                      1e18,
                                      makerAssetFilledAmount
                                  )
                              );
                              // Take taker fee.
                              TestMintableERC20Token takerFeeToken = _getTokenFromAssetData(order.takerFeeAssetData);
                              uint256 takerFee = LibMathV06.getPartialAmountFloor(
                                  takerAssetFillAmount,
                                  order.takerAssetAmount,
                                  order.takerFee
                              );
                              require(
                                  takerFeeToken.getSpendableAmount(msg.sender, address(this)) >= takerFee,
                                  "TestFillQuoteTransformerExchange/INSUFFICIENT_TAKER_FEE_FUNDS"
                              );
                              takerFeeToken.transferFrom(msg.sender, order.feeRecipientAddress, takerFee);
                              fillResults.makerAssetFilledAmount = makerAssetFilledAmount;
                              fillResults.takerAssetFilledAmount = takerAssetFillAmount;
                              fillResults.makerFeePaid = uint256(-1);
                              fillResults.takerFeePaid = takerFee;
                              fillResults.protocolFeePaid = protocolFee;
                          }
                          function encodeBehaviorData(FillBehavior calldata behavior)
                              external
                              pure
                              returns (bytes memory encoded)
                          {
                              return abi.encode(behavior);
                          }
                          function protocolFeeMultiplier()
                              external
                              pure
                              returns (uint256)
                          {
                              return PROTOCOL_FEE_MULTIPLIER;
                          }
                          function getAssetProxy(bytes4)
                              external
                              view
                              returns (address)
                          {
                              return address(this);
                          }
                          function _getTokenFromAssetData(bytes memory assetData)
                              private
                              pure
                              returns (TestMintableERC20Token token)
                          {
                              return TestMintableERC20Token(LibBytesV06.readAddress(assetData, 16));
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      contract TestMintableERC20Token {
                          mapping(address => uint256) public balanceOf;
                          mapping(address => mapping(address => uint256)) public allowance;
                          function transfer(address to, uint256 amount)
                              external
                              virtual
                              returns (bool)
                          {
                              return transferFrom(msg.sender, to, amount);
                          }
                          function approve(address spender, uint256 amount)
                              external
                              virtual
                              returns (bool)
                          {
                              allowance[msg.sender][spender] = amount;
                              return true;
                          }
                          function mint(address owner, uint256 amount)
                              external
                              virtual
                          {
                              balanceOf[owner] += amount;
                          }
                          function burn(address owner, uint256 amount)
                              external
                              virtual
                          {
                              require(balanceOf[owner] >= amount, "TestMintableERC20Token/INSUFFICIENT_FUNDS");
                              balanceOf[owner] -= amount;
                          }
                          function transferFrom(address from, address to, uint256 amount)
                              public
                              virtual
                              returns (bool)
                          {
                              if (from != msg.sender) {
                                  require(
                                      allowance[from][msg.sender] >= amount,
                                      "TestMintableERC20Token/INSUFFICIENT_ALLOWANCE"
                                  );
                                  allowance[from][msg.sender] -= amount;
                              }
                              require(balanceOf[from] >= amount, "TestMintableERC20Token/INSUFFICIENT_FUNDS");
                              balanceOf[from] -= amount;
                              balanceOf[to] += amount;
                              return true;
                          }
                          function getSpendableAmount(address owner, address spender)
                              external
                              view
                              returns (uint256)
                          {
                              return balanceOf[owner] < allowance[owner][spender]
                                  ? balanceOf[owner]
                                  : allowance[owner][spender];
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/transformers/IERC20Transformer.sol";
                      import "./TestMintableERC20Token.sol";
                      import "./TestTransformerHost.sol";
                      contract TestFillQuoteTransformerHost is
                          TestTransformerHost
                      {
                          function executeTransform(
                              IERC20Transformer transformer,
                              TestMintableERC20Token inputToken,
                              uint256 inputTokenAmount,
                              bytes calldata data
                          )
                              external
                              payable
                          {
                              if (inputTokenAmount != 0) {
                                  inputToken.mint(address(this), inputTokenAmount);
                              }
                              // Have to make this call externally because transformers aren't payable.
                              this.rawExecuteTransform(transformer, bytes32(0), msg.sender, data);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "../src/transformers/IERC20Transformer.sol";
                      import "../src/transformers/LibERC20Transformer.sol";
                      contract TestTransformerHost {
                          using LibERC20Transformer for IERC20TokenV06;
                          using LibRichErrorsV06 for bytes;
                          function rawExecuteTransform(
                              IERC20Transformer transformer,
                              bytes32 callDataHash,
                              address taker,
                              bytes calldata data
                          )
                              external
                          {
                              (bool _success, bytes memory resultData) =
                                  address(transformer).delegatecall(abi.encodeWithSelector(
                                      transformer.transform.selector,
                                      callDataHash,
                                      taker,
                                      data
                                  ));
                              if (!_success) {
                                  resultData.rrevert();
                              }
                              require(
                                  abi.decode(resultData, (bytes4)) == LibERC20Transformer.TRANSFORMER_SUCCESS,
                                  "TestTransformerHost/INVALID_TRANSFORMER_RESULT"
                              );
                          }
                          // solhint-disable
                          receive() external payable {}
                          // solhint-enable
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/ZeroEx.sol";
                      import "../src/features/IBootstrap.sol";
                      import "../src/migrations/FullMigration.sol";
                      contract TestFullMigration is
                          FullMigration
                      {
                          address public dieRecipient;
                          // solhint-disable-next-line no-empty-blocks
                          constructor(address payable deployer) public FullMigration(deployer) {}
                          function die(address payable ethRecipient) external override {
                              dieRecipient = ethRecipient;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/ZeroEx.sol";
                      import "../src/features/IBootstrap.sol";
                      import "../src/migrations/InitialMigration.sol";
                      contract TestInitialMigration is
                          InitialMigration
                      {
                          address public bootstrapFeature;
                          address public dieRecipient;
                          // solhint-disable-next-line no-empty-blocks
                          constructor(address deployer) public InitialMigration(deployer) {}
                          function callBootstrap(ZeroEx zeroEx) external {
                              IBootstrap(address(zeroEx)).bootstrap(address(this), new bytes(0));
                          }
                          function bootstrap(address owner, BootstrapFeatures memory features)
                              public
                              override
                              returns (bytes4 success)
                          {
                              success = InitialMigration.bootstrap(owner, features);
                              // Snoop the bootstrap feature contract.
                              bootstrapFeature = ZeroEx(address(uint160(address(this))))
                                  .getFunctionImplementation(IBootstrap.bootstrap.selector);
                          }
                          function die(address payable ethRecipient) public override {
                              dieRecipient = ethRecipient;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/features/TransformERC20.sol";
                      contract TestMetaTransactionsTransformERC20Feature is
                          TransformERC20
                      {
                          event TransformERC20Called(
                              address sender,
                              uint256 value,
                              bytes32 callDataHash,
                              address taker,
                              IERC20TokenV06 inputToken,
                              IERC20TokenV06 outputToken,
                              uint256 inputTokenAmount,
                              uint256 minOutputTokenAmount,
                              Transformation[] transformations
                          );
                          function _transformERC20(
                              bytes32 callDataHash,
                              address payable taker,
                              IERC20TokenV06 inputToken,
                              IERC20TokenV06 outputToken,
                              uint256 inputTokenAmount,
                              uint256 minOutputTokenAmount,
                              Transformation[] memory transformations
                          )
                              public
                              override
                              payable
                              returns (uint256 outputTokenAmount)
                          {
                              if (msg.value == 666) {
                                  revert('FAIL');
                              }
                              emit TransformERC20Called(
                                  msg.sender,
                                  msg.value,
                                  callDataHash,
                                  taker,
                                  inputToken,
                                  outputToken,
                                  inputTokenAmount,
                                  minOutputTokenAmount,
                                  transformations
                              );
                              return 1337;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/migrations/LibMigrate.sol";
                      import "../src/features/IOwnable.sol";
                      contract TestMigrator {
                          event TestMigrateCalled(
                              bytes callData,
                              address owner
                          );
                          function succeedingMigrate() external returns (bytes4 success) {
                              emit TestMigrateCalled(
                                  msg.data,
                                  IOwnable(address(this)).owner()
                              );
                              return LibMigrate.MIGRATE_SUCCESS;
                          }
                          function failingMigrate() external returns (bytes4 success) {
                              emit TestMigrateCalled(
                                  msg.data,
                                  IOwnable(address(this)).owner()
                              );
                              return 0xdeadbeef;
                          }
                          function revertingMigrate() external pure {
                              revert("OOPSIE");
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "../src/transformers/IERC20Transformer.sol";
                      import "../src/transformers/LibERC20Transformer.sol";
                      import "./TestMintableERC20Token.sol";
                      contract TestMintTokenERC20Transformer is
                          IERC20Transformer
                      {
                          struct TransformData {
                              IERC20TokenV06 inputToken;
                              TestMintableERC20Token outputToken;
                              uint256 burnAmount;
                              uint256 mintAmount;
                              uint256 feeAmount;
                          }
                          event MintTransform(
                              address context,
                              address caller,
                              bytes32 callDataHash,
                              address taker,
                              bytes data,
                              uint256 inputTokenBalance,
                              uint256 ethBalance
                          );
                          function transform(
                              bytes32 callDataHash,
                              address payable taker,
                              bytes calldata data_
                          )
                              external
                              override
                              returns (bytes4 success)
                          {
                              TransformData memory data = abi.decode(data_, (TransformData));
                              emit MintTransform(
                                  address(this),
                                  msg.sender,
                                  callDataHash,
                                  taker,
                                  data_,
                                  data.inputToken.balanceOf(address(this)),
                                  address(this).balance
                              );
                              // "Burn" input tokens.
                              data.inputToken.transfer(address(0), data.burnAmount);
                              // Mint output tokens.
                              if (LibERC20Transformer.isTokenETH(IERC20TokenV06(address(data.outputToken)))) {
                                  taker.transfer(data.mintAmount);
                              } else {
                                  data.outputToken.mint(
                                      taker,
                                      data.mintAmount
                                  );
                                  // Burn fees from output.
                                  data.outputToken.burn(taker, data.feeAmount);
                              }
                              return LibERC20Transformer.TRANSFORMER_SUCCESS;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/fixins/FixinCommon.sol";
                      contract TestSimpleFunctionRegistryFeatureImpl1 is
                          FixinCommon
                      {
                          function testFn()
                              external
                              pure
                              returns (uint256 id)
                          {
                              return 1337;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/fixins/FixinCommon.sol";
                      contract TestSimpleFunctionRegistryFeatureImpl2 is
                          FixinCommon
                      {
                          function testFn()
                              external
                              pure
                              returns (uint256 id)
                          {
                              return 1338;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/features/TokenSpender.sol";
                      contract TestTokenSpender is
                          TokenSpender
                      {
                          modifier onlySelf() override {
                              _;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./TestMintableERC20Token.sol";
                      contract TestTokenSpenderERC20Token is
                          TestMintableERC20Token
                      {
                          event TransferFromCalled(
                              address sender,
                              address from,
                              address to,
                              uint256 amount
                          );
                          // `transferFrom()` behavior depends on the value of `amount`.
                          uint256 constant private EMPTY_RETURN_AMOUNT = 1337;
                          uint256 constant private FALSE_RETURN_AMOUNT = 1338;
                          uint256 constant private REVERT_RETURN_AMOUNT = 1339;
                          function transferFrom(address from, address to, uint256 amount)
                              public
                              override
                              returns (bool)
                          {
                              emit TransferFromCalled(msg.sender, from, to, amount);
                              if (amount == EMPTY_RETURN_AMOUNT) {
                                  assembly { return(0, 0) }
                              }
                              if (amount == FALSE_RETURN_AMOUNT) {
                                  return false;
                              }
                              if (amount == REVERT_RETURN_AMOUNT) {
                                  revert("TestTokenSpenderERC20Token/Revert");
                              }
                              return true;
                          }
                          function setBalanceAndAllowanceOf(
                              address owner,
                              uint256 balance,
                              address spender,
                              uint256 allowance_
                          )
                              external
                          {
                              balanceOf[owner] = balance;
                              allowance[owner][spender] = allowance_;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/features/TransformERC20.sol";
                      contract TestTransformERC20 is
                          TransformERC20
                      {
                          // solhint-disable no-empty-blocks
                          constructor()
                              TransformERC20()
                              public
                          {}
                          modifier onlySelf() override {
                              _;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/transformers/Transformer.sol";
                      import "../src/transformers/LibERC20Transformer.sol";
                      contract TestTransformerBase is
                          Transformer
                      {
                          function transform(
                              bytes32,
                              address payable,
                              bytes calldata
                          )
                              external
                              override
                              returns (bytes4 success)
                          {
                              return LibERC20Transformer.TRANSFORMER_SUCCESS;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/transformers/LibERC20Transformer.sol";
                      contract TestTransformerDeployerTransformer {
                          address payable public immutable deployer;
                          constructor() public payable {
                              deployer = msg.sender;
                          }
                          modifier onlyDeployer() {
                              require(msg.sender == deployer, "TestTransformerDeployerTransformer/ONLY_DEPLOYER");
                              _;
                          }
                          function die()
                              external
                              onlyDeployer
                          {
                              selfdestruct(deployer);
                          }
                          function isDeployedByDeployer(uint32 nonce)
                              external
                              view
                              returns (bool)
                          {
                              return LibERC20Transformer.getDeployedAddress(deployer, nonce) == address(this);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./TestMintableERC20Token.sol";
                      contract TestWeth is
                          TestMintableERC20Token
                      {
                          function deposit()
                              external
                              payable
                          {
                              this.mint(msg.sender, msg.value);
                          }
                          function withdraw(uint256 amount)
                              external
                          {
                              require(balanceOf[msg.sender] >= amount, "TestWeth/INSUFFICIENT_FUNDS");
                              balanceOf[msg.sender] -= amount;
                              msg.sender.transfer(amount);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/transformers/IERC20Transformer.sol";
                      import "./TestMintableERC20Token.sol";
                      import "./TestTransformerHost.sol";
                      import "./TestWeth.sol";
                      contract TestWethTransformerHost is
                          TestTransformerHost
                      {
                          // solhint-disable
                          TestWeth private immutable _weth;
                          // solhint-enable
                          constructor(TestWeth weth) public {
                              _weth = weth;
                          }
                          function executeTransform(
                              uint256 wethAmount,
                              IERC20Transformer transformer,
                              bytes calldata data
                          )
                              external
                              payable
                          {
                              if (wethAmount != 0) {
                                  _weth.deposit{value: wethAmount}();
                              }
                              // Have to make this call externally because transformers aren't payable.
                              this.rawExecuteTransform(transformer, bytes32(0), msg.sender, data);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/fixins/FixinCommon.sol";
                      import "../src/ZeroEx.sol";
                      contract TestZeroExFeature is
                          FixinCommon
                      {
                          event PayableFnCalled(uint256 value);
                          event NotPayableFnCalled();
                          function payableFn()
                              external
                              payable
                          {
                              emit PayableFnCalled(msg.value);
                          }
                          function notPayableFn()
                              external
                          {
                              emit NotPayableFnCalled();
                          }
                          // solhint-disable no-empty-blocks
                          function unimplmentedFn()
                              external
                          {}
                          function internalFn()
                              external
                              onlySelf
                          {}
                      }
                      

                      File 6 of 12: ZeroEx
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
                      import "./migrations/LibBootstrap.sol";
                      import "./features/Bootstrap.sol";
                      import "./storage/LibProxyStorage.sol";
                      import "./errors/LibProxyRichErrors.sol";
                      /// @dev An extensible proxy contract that serves as a universal entry point for
                      ///      interacting with the 0x protocol.
                      contract ZeroEx {
                          // solhint-disable separate-by-one-line-in-contract,indent,var-name-mixedcase
                          using LibBytesV06 for bytes;
                          /// @dev Construct this contract and register the `Bootstrap` feature.
                          ///      After constructing this contract, `bootstrap()` should be called
                          ///      to seed the initial feature set.
                          constructor() public {
                              // Temporarily create and register the bootstrap feature.
                              // It will deregister itself after `bootstrap()` has been called.
                              Bootstrap bootstrap = new Bootstrap(msg.sender);
                              LibProxyStorage.getStorage().impls[bootstrap.bootstrap.selector] =
                                  address(bootstrap);
                          }
                          // solhint-disable state-visibility
                          /// @dev Forwards calls to the appropriate implementation contract.
                          fallback() external payable {
                              bytes4 selector = msg.data.readBytes4(0);
                              address impl = getFunctionImplementation(selector);
                              if (impl == address(0)) {
                                  _revertWithData(LibProxyRichErrors.NotImplementedError(selector));
                              }
                              (bool success, bytes memory resultData) = impl.delegatecall(msg.data);
                              if (!success) {
                                  _revertWithData(resultData);
                              }
                              _returnWithData(resultData);
                          }
                          /// @dev Fallback for just receiving ether.
                          receive() external payable {}
                          // solhint-enable state-visibility
                          /// @dev Get the implementation contract of a registered function.
                          /// @param selector The function selector.
                          /// @return impl The implementation contract address.
                          function getFunctionImplementation(bytes4 selector)
                              public
                              view
                              returns (address impl)
                          {
                              return LibProxyStorage.getStorage().impls[selector];
                          }
                          /// @dev Revert with arbitrary bytes.
                          /// @param data Revert data.
                          function _revertWithData(bytes memory data) private pure {
                              assembly { revert(add(data, 32), mload(data)) }
                          }
                          /// @dev Return with arbitrary bytes.
                          /// @param data Return data.
                          function _returnWithData(bytes memory data) private pure {
                              assembly { return(add(data, 32), mload(data)) }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./errors/LibBytesRichErrorsV06.sol";
                      import "./errors/LibRichErrorsV06.sol";
                      library LibBytesV06 {
                          using LibBytesV06 for bytes;
                          /// @dev Gets the memory address for a byte array.
                          /// @param input Byte array to lookup.
                          /// @return memoryAddress Memory address of byte array. This
                          ///         points to the header of the byte array which contains
                          ///         the length.
                          function rawAddress(bytes memory input)
                              internal
                              pure
                              returns (uint256 memoryAddress)
                          {
                              assembly {
                                  memoryAddress := input
                              }
                              return memoryAddress;
                          }
                          /// @dev Gets the memory address for the contents of a byte array.
                          /// @param input Byte array to lookup.
                          /// @return memoryAddress Memory address of the contents of the byte array.
                          function contentAddress(bytes memory input)
                              internal
                              pure
                              returns (uint256 memoryAddress)
                          {
                              assembly {
                                  memoryAddress := add(input, 32)
                              }
                              return memoryAddress;
                          }
                          /// @dev Copies `length` bytes from memory location `source` to `dest`.
                          /// @param dest memory address to copy bytes to.
                          /// @param source memory address to copy bytes from.
                          /// @param length number of bytes to copy.
                          function memCopy(
                              uint256 dest,
                              uint256 source,
                              uint256 length
                          )
                              internal
                              pure
                          {
                              if (length < 32) {
                                  // Handle a partial word by reading destination and masking
                                  // off the bits we are interested in.
                                  // This correctly handles overlap, zero lengths and source == dest
                                  assembly {
                                      let mask := sub(exp(256, sub(32, length)), 1)
                                      let s := and(mload(source), not(mask))
                                      let d := and(mload(dest), mask)
                                      mstore(dest, or(s, d))
                                  }
                              } else {
                                  // Skip the O(length) loop when source == dest.
                                  if (source == dest) {
                                      return;
                                  }
                                  // For large copies we copy whole words at a time. The final
                                  // word is aligned to the end of the range (instead of after the
                                  // previous) to handle partial words. So a copy will look like this:
                                  //
                                  //  ####
                                  //      ####
                                  //          ####
                                  //            ####
                                  //
                                  // We handle overlap in the source and destination range by
                                  // changing the copying direction. This prevents us from
                                  // overwriting parts of source that we still need to copy.
                                  //
                                  // This correctly handles source == dest
                                  //
                                  if (source > dest) {
                                      assembly {
                                          // We subtract 32 from `sEnd` and `dEnd` because it
                                          // is easier to compare with in the loop, and these
                                          // are also the addresses we need for copying the
                                          // last bytes.
                                          length := sub(length, 32)
                                          let sEnd := add(source, length)
                                          let dEnd := add(dest, length)
                                          // Remember the last 32 bytes of source
                                          // This needs to be done here and not after the loop
                                          // because we may have overwritten the last bytes in
                                          // source already due to overlap.
                                          let last := mload(sEnd)
                                          // Copy whole words front to back
                                          // Note: the first check is always true,
                                          // this could have been a do-while loop.
                                          // solhint-disable-next-line no-empty-blocks
                                          for {} lt(source, sEnd) {} {
                                              mstore(dest, mload(source))
                                              source := add(source, 32)
                                              dest := add(dest, 32)
                                          }
                                          // Write the last 32 bytes
                                          mstore(dEnd, last)
                                      }
                                  } else {
                                      assembly {
                                          // We subtract 32 from `sEnd` and `dEnd` because those
                                          // are the starting points when copying a word at the end.
                                          length := sub(length, 32)
                                          let sEnd := add(source, length)
                                          let dEnd := add(dest, length)
                                          // Remember the first 32 bytes of source
                                          // This needs to be done here and not after the loop
                                          // because we may have overwritten the first bytes in
                                          // source already due to overlap.
                                          let first := mload(source)
                                          // Copy whole words back to front
                                          // We use a signed comparisson here to allow dEnd to become
                                          // negative (happens when source and dest < 32). Valid
                                          // addresses in local memory will never be larger than
                                          // 2**255, so they can be safely re-interpreted as signed.
                                          // Note: the first check is always true,
                                          // this could have been a do-while loop.
                                          // solhint-disable-next-line no-empty-blocks
                                          for {} slt(dest, dEnd) {} {
                                              mstore(dEnd, mload(sEnd))
                                              sEnd := sub(sEnd, 32)
                                              dEnd := sub(dEnd, 32)
                                          }
                                          // Write the first 32 bytes
                                          mstore(dest, first)
                                      }
                                  }
                              }
                          }
                          /// @dev Returns a slices from a byte array.
                          /// @param b The byte array to take a slice from.
                          /// @param from The starting index for the slice (inclusive).
                          /// @param to The final index for the slice (exclusive).
                          /// @return result The slice containing bytes at indices [from, to)
                          function slice(
                              bytes memory b,
                              uint256 from,
                              uint256 to
                          )
                              internal
                              pure
                              returns (bytes memory result)
                          {
                              // Ensure that the from and to positions are valid positions for a slice within
                              // the byte array that is being used.
                              if (from > to) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
                                      from,
                                      to
                                  ));
                              }
                              if (to > b.length) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
                                      to,
                                      b.length
                                  ));
                              }
                              // Create a new bytes structure and copy contents
                              result = new bytes(to - from);
                              memCopy(
                                  result.contentAddress(),
                                  b.contentAddress() + from,
                                  result.length
                              );
                              return result;
                          }
                          /// @dev Returns a slice from a byte array without preserving the input.
                          ///      When `from == 0`, the original array will match the slice.
                          ///      In other cases its state will be corrupted.
                          /// @param b The byte array to take a slice from. Will be destroyed in the process.
                          /// @param from The starting index for the slice (inclusive).
                          /// @param to The final index for the slice (exclusive).
                          /// @return result The slice containing bytes at indices [from, to)
                          function sliceDestructive(
                              bytes memory b,
                              uint256 from,
                              uint256 to
                          )
                              internal
                              pure
                              returns (bytes memory result)
                          {
                              // Ensure that the from and to positions are valid positions for a slice within
                              // the byte array that is being used.
                              if (from > to) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
                                      from,
                                      to
                                  ));
                              }
                              if (to > b.length) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
                                      to,
                                      b.length
                                  ));
                              }
                              // Create a new bytes structure around [from, to) in-place.
                              assembly {
                                  result := add(b, from)
                                  mstore(result, sub(to, from))
                              }
                              return result;
                          }
                          /// @dev Pops the last byte off of a byte array by modifying its length.
                          /// @param b Byte array that will be modified.
                          /// @return result The byte that was popped off.
                          function popLastByte(bytes memory b)
                              internal
                              pure
                              returns (bytes1 result)
                          {
                              if (b.length == 0) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanZeroRequired,
                                      b.length,
                                      0
                                  ));
                              }
                              // Store last byte.
                              result = b[b.length - 1];
                              assembly {
                                  // Decrement length of byte array.
                                  let newLen := sub(mload(b), 1)
                                  mstore(b, newLen)
                              }
                              return result;
                          }
                          /// @dev Tests equality of two byte arrays.
                          /// @param lhs First byte array to compare.
                          /// @param rhs Second byte array to compare.
                          /// @return equal True if arrays are the same. False otherwise.
                          function equals(
                              bytes memory lhs,
                              bytes memory rhs
                          )
                              internal
                              pure
                              returns (bool equal)
                          {
                              // Keccak gas cost is 30 + numWords * 6. This is a cheap way to compare.
                              // We early exit on unequal lengths, but keccak would also correctly
                              // handle this.
                              return lhs.length == rhs.length && keccak256(lhs) == keccak256(rhs);
                          }
                          /// @dev Reads an address from a position in a byte array.
                          /// @param b Byte array containing an address.
                          /// @param index Index in byte array of address.
                          /// @return result address from byte array.
                          function readAddress(
                              bytes memory b,
                              uint256 index
                          )
                              internal
                              pure
                              returns (address result)
                          {
                              if (b.length < index + 20) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
                                      b.length,
                                      index + 20 // 20 is length of address
                                  ));
                              }
                              // Add offset to index:
                              // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
                              // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
                              index += 20;
                              // Read address from array memory
                              assembly {
                                  // 1. Add index to address of bytes array
                                  // 2. Load 32-byte word from memory
                                  // 3. Apply 20-byte mask to obtain address
                                  result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff)
                              }
                              return result;
                          }
                          /// @dev Writes an address into a specific position in a byte array.
                          /// @param b Byte array to insert address into.
                          /// @param index Index in byte array of address.
                          /// @param input Address to put into byte array.
                          function writeAddress(
                              bytes memory b,
                              uint256 index,
                              address input
                          )
                              internal
                              pure
                          {
                              if (b.length < index + 20) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
                                      b.length,
                                      index + 20 // 20 is length of address
                                  ));
                              }
                              // Add offset to index:
                              // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
                              // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
                              index += 20;
                              // Store address into array memory
                              assembly {
                                  // The address occupies 20 bytes and mstore stores 32 bytes.
                                  // First fetch the 32-byte word where we'll be storing the address, then
                                  // apply a mask so we have only the bytes in the word that the address will not occupy.
                                  // Then combine these bytes with the address and store the 32 bytes back to memory with mstore.
                                  // 1. Add index to address of bytes array
                                  // 2. Load 32-byte word from memory
                                  // 3. Apply 12-byte mask to obtain extra bytes occupying word of memory where we'll store the address
                                  let neighbors := and(
                                      mload(add(b, index)),
                                      0xffffffffffffffffffffffff0000000000000000000000000000000000000000
                                  )
                                  // Make sure input address is clean.
                                  // (Solidity does not guarantee this)
                                  input := and(input, 0xffffffffffffffffffffffffffffffffffffffff)
                                  // Store the neighbors and address into memory
                                  mstore(add(b, index), xor(input, neighbors))
                              }
                          }
                          /// @dev Reads a bytes32 value from a position in a byte array.
                          /// @param b Byte array containing a bytes32 value.
                          /// @param index Index in byte array of bytes32 value.
                          /// @return result bytes32 value from byte array.
                          function readBytes32(
                              bytes memory b,
                              uint256 index
                          )
                              internal
                              pure
                              returns (bytes32 result)
                          {
                              if (b.length < index + 32) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
                                      b.length,
                                      index + 32
                                  ));
                              }
                              // Arrays are prefixed by a 256 bit length parameter
                              index += 32;
                              // Read the bytes32 from array memory
                              assembly {
                                  result := mload(add(b, index))
                              }
                              return result;
                          }
                          /// @dev Writes a bytes32 into a specific position in a byte array.
                          /// @param b Byte array to insert <input> into.
                          /// @param index Index in byte array of <input>.
                          /// @param input bytes32 to put into byte array.
                          function writeBytes32(
                              bytes memory b,
                              uint256 index,
                              bytes32 input
                          )
                              internal
                              pure
                          {
                              if (b.length < index + 32) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
                                      b.length,
                                      index + 32
                                  ));
                              }
                              // Arrays are prefixed by a 256 bit length parameter
                              index += 32;
                              // Read the bytes32 from array memory
                              assembly {
                                  mstore(add(b, index), input)
                              }
                          }
                          /// @dev Reads a uint256 value from a position in a byte array.
                          /// @param b Byte array containing a uint256 value.
                          /// @param index Index in byte array of uint256 value.
                          /// @return result uint256 value from byte array.
                          function readUint256(
                              bytes memory b,
                              uint256 index
                          )
                              internal
                              pure
                              returns (uint256 result)
                          {
                              result = uint256(readBytes32(b, index));
                              return result;
                          }
                          /// @dev Writes a uint256 into a specific position in a byte array.
                          /// @param b Byte array to insert <input> into.
                          /// @param index Index in byte array of <input>.
                          /// @param input uint256 to put into byte array.
                          function writeUint256(
                              bytes memory b,
                              uint256 index,
                              uint256 input
                          )
                              internal
                              pure
                          {
                              writeBytes32(b, index, bytes32(input));
                          }
                          /// @dev Reads an unpadded bytes4 value from a position in a byte array.
                          /// @param b Byte array containing a bytes4 value.
                          /// @param index Index in byte array of bytes4 value.
                          /// @return result bytes4 value from byte array.
                          function readBytes4(
                              bytes memory b,
                              uint256 index
                          )
                              internal
                              pure
                              returns (bytes4 result)
                          {
                              if (b.length < index + 4) {
                                  LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError(
                                      LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
                                      b.length,
                                      index + 4
                                  ));
                              }
                              // Arrays are prefixed by a 32 byte length field
                              index += 32;
                              // Read the bytes4 from array memory
                              assembly {
                                  result := mload(add(b, index))
                                  // Solidity does not require us to clean the trailing bytes.
                                  // We do it anyway
                                  result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
                              }
                              return result;
                          }
                          /// @dev Writes a new length to a byte array.
                          ///      Decreasing length will lead to removing the corresponding lower order bytes from the byte array.
                          ///      Increasing length may lead to appending adjacent in-memory bytes to the end of the byte array.
                          /// @param b Bytes array to write new length to.
                          /// @param length New length of byte array.
                          function writeLength(bytes memory b, uint256 length)
                              internal
                              pure
                          {
                              assembly {
                                  mstore(b, length)
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibBytesRichErrorsV06 {
                          enum InvalidByteOperationErrorCodes {
                              FromLessThanOrEqualsToRequired,
                              ToLessThanOrEqualsLengthRequired,
                              LengthGreaterThanZeroRequired,
                              LengthGreaterThanOrEqualsFourRequired,
                              LengthGreaterThanOrEqualsTwentyRequired,
                              LengthGreaterThanOrEqualsThirtyTwoRequired,
                              LengthGreaterThanOrEqualsNestedBytesLengthRequired,
                              DestinationLengthGreaterThanOrEqualSourceLengthRequired
                          }
                          // bytes4(keccak256("InvalidByteOperationError(uint8,uint256,uint256)"))
                          bytes4 internal constant INVALID_BYTE_OPERATION_ERROR_SELECTOR =
                              0x28006595;
                          // solhint-disable func-name-mixedcase
                          function InvalidByteOperationError(
                              InvalidByteOperationErrorCodes errorCode,
                              uint256 offset,
                              uint256 required
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  INVALID_BYTE_OPERATION_ERROR_SELECTOR,
                                  errorCode,
                                  offset,
                                  required
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibRichErrorsV06 {
                          // bytes4(keccak256("Error(string)"))
                          bytes4 internal constant STANDARD_ERROR_SELECTOR = 0x08c379a0;
                          // solhint-disable func-name-mixedcase
                          /// @dev ABI encode a standard, string revert error payload.
                          ///      This is the same payload that would be included by a `revert(string)`
                          ///      solidity statement. It has the function signature `Error(string)`.
                          /// @param message The error string.
                          /// @return The ABI encoded error.
                          function StandardError(string memory message)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  STANDARD_ERROR_SELECTOR,
                                  bytes(message)
                              );
                          }
                          // solhint-enable func-name-mixedcase
                          /// @dev Reverts an encoded rich revert reason `errorData`.
                          /// @param errorData ABI encoded error data.
                          function rrevert(bytes memory errorData)
                              internal
                              pure
                          {
                              assembly {
                                  revert(add(errorData, 0x20), mload(errorData))
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../errors/LibProxyRichErrors.sol";
                      library LibBootstrap {
                          /// @dev Magic bytes returned by the bootstrapper to indicate success.
                          ///      This is `keccack('BOOTSTRAP_SUCCESS')`.
                          bytes4 internal constant BOOTSTRAP_SUCCESS = 0xd150751b;
                          using LibRichErrorsV06 for bytes;
                          /// @dev Perform a delegatecall and ensure it returns the magic bytes.
                          /// @param target The call target.
                          /// @param data The call data.
                          function delegatecallBootstrapFunction(
                              address target,
                              bytes memory data
                          )
                              internal
                          {
                              (bool success, bytes memory resultData) = target.delegatecall(data);
                              if (!success ||
                                  resultData.length != 32 ||
                                  abi.decode(resultData, (bytes4)) != BOOTSTRAP_SUCCESS)
                              {
                                  LibProxyRichErrors.BootstrapCallFailedError(target, resultData).rrevert();
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibProxyRichErrors {
                          // solhint-disable func-name-mixedcase
                          function NotImplementedError(bytes4 selector)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("NotImplementedError(bytes4)")),
                                  selector
                              );
                          }
                          function InvalidBootstrapCallerError(address actual, address expected)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InvalidBootstrapCallerError(address,address)")),
                                  actual,
                                  expected
                              );
                          }
                          function InvalidDieCallerError(address actual, address expected)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InvalidDieCallerError(address,address)")),
                                  actual,
                                  expected
                              );
                          }
                          function BootstrapCallFailedError(address target, bytes memory resultData)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("BootstrapCallFailedError(address,bytes)")),
                                  target,
                                  resultData
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../migrations/LibBootstrap.sol";
                      import "../storage/LibProxyStorage.sol";
                      import "./IBootstrap.sol";
                      /// @dev Detachable `bootstrap()` feature.
                      contract Bootstrap is
                          IBootstrap
                      {
                          // solhint-disable state-visibility,indent
                          /// @dev The ZeroEx contract.
                          ///      This has to be immutable to persist across delegatecalls.
                          address immutable private _deployer;
                          /// @dev The implementation address of this contract.
                          ///      This has to be immutable to persist across delegatecalls.
                          address immutable private _implementation;
                          /// @dev The deployer.
                          ///      This has to be immutable to persist across delegatecalls.
                          address immutable private _bootstrapCaller;
                          // solhint-enable state-visibility,indent
                          using LibRichErrorsV06 for bytes;
                          /// @dev Construct this contract and set the bootstrap migration contract.
                          ///      After constructing this contract, `bootstrap()` should be called
                          ///      to seed the initial feature set.
                          /// @param bootstrapCaller The allowed caller of `bootstrap()`.
                          constructor(address bootstrapCaller) public {
                              _deployer = msg.sender;
                              _implementation = address(this);
                              _bootstrapCaller = bootstrapCaller;
                          }
                          /// @dev Bootstrap the initial feature set of this contract by delegatecalling
                          ///      into `target`. Before exiting the `bootstrap()` function will
                          ///      deregister itself from the proxy to prevent being called again.
                          /// @param target The bootstrapper contract address.
                          /// @param callData The call data to execute on `target`.
                          function bootstrap(address target, bytes calldata callData) external override {
                              // Only the bootstrap caller can call this function.
                              if (msg.sender != _bootstrapCaller) {
                                  LibProxyRichErrors.InvalidBootstrapCallerError(
                                      msg.sender,
                                      _bootstrapCaller
                                  ).rrevert();
                              }
                              // Deregister.
                              LibProxyStorage.getStorage().impls[this.bootstrap.selector] = address(0);
                              // Self-destruct.
                              Bootstrap(_implementation).die();
                              // Call the bootstrapper.
                              LibBootstrap.delegatecallBootstrapFunction(target, callData);
                          }
                          /// @dev Self-destructs this contract.
                          ///      Can only be called by the deployer.
                          function die() external {
                              if (msg.sender != _deployer) {
                                  LibProxyRichErrors.InvalidDieCallerError(msg.sender, _deployer).rrevert();
                              }
                              selfdestruct(msg.sender);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./LibStorage.sol";
                      /// @dev Storage helpers for the proxy contract.
                      library LibProxyStorage {
                          /// @dev Storage bucket for proxy contract.
                          struct Storage {
                              // Mapping of function selector -> function implementation
                              mapping(bytes4 => address) impls;
                              // The owner of the proxy contract.
                              address owner;
                          }
                          /// @dev Get the storage bucket for this contract.
                          function getStorage() internal pure returns (Storage storage stor) {
                              uint256 storageSlot = LibStorage.getStorageSlot(
                                  LibStorage.StorageId.Proxy
                              );
                              // Dip into assembly to change the slot pointed to by the local
                              // variable `stor`.
                              // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
                              assembly { stor_slot := storageSlot }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      /// @dev Common storage helpers
                      library LibStorage {
                          /// @dev What to bit-shift a storage ID by to get its slot.
                          ///      This gives us a maximum of 2**128 inline fields in each bucket.
                          uint256 private constant STORAGE_SLOT_EXP = 128;
                          /// @dev Storage IDs for feature storage buckets.
                          ///      WARNING: APPEND-ONLY.
                          enum StorageId {
                              Proxy,
                              SimpleFunctionRegistry,
                              Ownable,
                              TokenSpender,
                              TransformERC20
                          }
                          /// @dev Get the storage slot given a storage ID. We assign unique, well-spaced
                          ///     slots to storage bucket variables to ensure they do not overlap.
                          ///     See: https://solidity.readthedocs.io/en/v0.6.6/assembly.html#access-to-external-variables-functions-and-libraries
                          /// @param storageId An entry in `StorageId`
                          /// @return slot The storage slot.
                          function getStorageSlot(StorageId storageId)
                              internal
                              pure
                              returns (uint256 slot)
                          {
                              // This should never overflow with a reasonable `STORAGE_SLOT_EXP`
                              // because Solidity will do a range check on `storageId` during the cast.
                              return (uint256(storageId) + 1) << STORAGE_SLOT_EXP;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      /// @dev Detachable `bootstrap()` feature.
                      interface IBootstrap {
                          /// @dev Bootstrap the initial feature set of this contract by delegatecalling
                          ///      into `target`. Before exiting the `bootstrap()` function will
                          ///      deregister itself from the proxy to prevent being called again.
                          /// @param target The bootstrapper contract address.
                          /// @param callData The call data to execute on `target`.
                          function bootstrap(address target, bytes calldata callData) external;
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibCommonRichErrors {
                          // solhint-disable func-name-mixedcase
                          function OnlyCallableBySelfError(address sender)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("OnlyCallableBySelfError(address)")),
                                  sender
                              );
                          }
                          function IllegalReentrancyError()
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("IllegalReentrancyError()"))
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibOwnableRichErrors {
                          // solhint-disable func-name-mixedcase
                          function OnlyOwnerError(
                              address sender,
                              address owner
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("OnlyOwnerError(address,address)")),
                                  sender,
                                  owner
                              );
                          }
                          function TransferOwnerToZeroError()
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("TransferOwnerToZeroError()"))
                              );
                          }
                          function MigrateCallFailedError(address target, bytes memory resultData)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("MigrateCallFailedError(address,bytes)")),
                                  target,
                                  resultData
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibSimpleFunctionRegistryRichErrors {
                          // solhint-disable func-name-mixedcase
                          function NotInRollbackHistoryError(bytes4 selector, address targetImpl)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("NotInRollbackHistoryError(bytes4,address)")),
                                  selector,
                                  targetImpl
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibSpenderRichErrors {
                          // solhint-disable func-name-mixedcase
                          function SpenderERC20TransferFromFailedError(
                              address token,
                              address owner,
                              address to,
                              uint256 amount,
                              bytes memory errorData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("SpenderERC20TransferFromFailedError(address,address,address,uint256,bytes)")),
                                  token,
                                  owner,
                                  to,
                                  amount,
                                  errorData
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibTransformERC20RichErrors {
                          // solhint-disable func-name-mixedcase,separate-by-one-line-in-contract
                          function InsufficientEthAttachedError(
                              uint256 ethAttached,
                              uint256 ethNeeded
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InsufficientEthAttachedError(uint256,uint256)")),
                                  ethAttached,
                                  ethNeeded
                              );
                          }
                          function IncompleteTransformERC20Error(
                              address outputToken,
                              uint256 outputTokenAmount,
                              uint256 minOutputTokenAmount
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("IncompleteTransformERC20Error(address,uint256,uint256)")),
                                  outputToken,
                                  outputTokenAmount,
                                  minOutputTokenAmount
                              );
                          }
                          function NegativeTransformERC20OutputError(
                              address outputToken,
                              uint256 outputTokenLostAmount
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("NegativeTransformERC20OutputError(address,uint256)")),
                                  outputToken,
                                  outputTokenLostAmount
                              );
                          }
                          function TransformerFailedError(
                              address transformer,
                              bytes memory transformerData,
                              bytes memory resultData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("TransformerFailedError(address,bytes,bytes)")),
                                  transformer,
                                  transformerData,
                                  resultData
                              );
                          }
                          // Common Transformer errors ///////////////////////////////////////////////
                          function OnlyCallableByDeployerError(
                              address caller,
                              address deployer
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("OnlyCallableByDeployerError(address,address)")),
                                  caller,
                                  deployer
                              );
                          }
                          function InvalidExecutionContextError(
                              address actualContext,
                              address expectedContext
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InvalidExecutionContextError(address,address)")),
                                  actualContext,
                                  expectedContext
                              );
                          }
                          enum InvalidTransformDataErrorCode {
                              INVALID_TOKENS,
                              INVALID_ARRAY_LENGTH
                          }
                          function InvalidTransformDataError(
                              InvalidTransformDataErrorCode errorCode,
                              bytes memory transformData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InvalidTransformDataError(uint8,bytes)")),
                                  errorCode,
                                  transformData
                              );
                          }
                          // FillQuoteTransformer errors /////////////////////////////////////////////
                          function IncompleteFillSellQuoteError(
                              address sellToken,
                              uint256 soldAmount,
                              uint256 sellAmount
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("IncompleteFillSellQuoteError(address,uint256,uint256)")),
                                  sellToken,
                                  soldAmount,
                                  sellAmount
                              );
                          }
                          function IncompleteFillBuyQuoteError(
                              address buyToken,
                              uint256 boughtAmount,
                              uint256 buyAmount
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("IncompleteFillBuyQuoteError(address,uint256,uint256)")),
                                  buyToken,
                                  boughtAmount,
                                  buyAmount
                              );
                          }
                          function InsufficientTakerTokenError(
                              uint256 tokenBalance,
                              uint256 tokensNeeded
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InsufficientTakerTokenError(uint256,uint256)")),
                                  tokenBalance,
                                  tokensNeeded
                              );
                          }
                          function InsufficientProtocolFeeError(
                              uint256 ethBalance,
                              uint256 ethNeeded
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InsufficientProtocolFeeError(uint256,uint256)")),
                                  ethBalance,
                                  ethNeeded
                              );
                          }
                          function InvalidERC20AssetDataError(
                              bytes memory assetData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InvalidERC20AssetDataError(bytes)")),
                                  assetData
                              );
                          }
                          function InvalidTakerFeeTokenError(
                              address token
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("InvalidTakerFeeTokenError(address)")),
                                  token
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibWalletRichErrors {
                          // solhint-disable func-name-mixedcase
                          function WalletExecuteCallFailedError(
                              address wallet,
                              address callTarget,
                              bytes memory callData,
                              uint256 callValue,
                              bytes memory errorData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("WalletExecuteCallFailedError(address,address,bytes,uint256,bytes)")),
                                  wallet,
                                  callTarget,
                                  callData,
                                  callValue,
                                  errorData
                              );
                          }
                          function WalletExecuteDelegateCallFailedError(
                              address wallet,
                              address callTarget,
                              bytes memory callData,
                              bytes memory errorData
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  bytes4(keccak256("WalletExecuteDelegateCallFailedError(address,address,bytes,bytes)")),
                                  wallet,
                                  callTarget,
                                  callData,
                                  errorData
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/AuthorizableV06.sol";
                      import "../errors/LibSpenderRichErrors.sol";
                      import "./IAllowanceTarget.sol";
                      /// @dev The allowance target for the TokenSpender feature.
                      contract AllowanceTarget is
                          IAllowanceTarget,
                          AuthorizableV06
                      {
                          // solhint-disable no-unused-vars,indent,no-empty-blocks
                          using LibRichErrorsV06 for bytes;
                          /// @dev Execute an arbitrary call. Only an authority can call this.
                          /// @param target The call target.
                          /// @param callData The call data.
                          /// @return resultData The data returned by the call.
                          function executeCall(
                              address payable target,
                              bytes calldata callData
                          )
                              external
                              override
                              onlyAuthorized
                              returns (bytes memory resultData)
                          {
                              bool success;
                              (success, resultData) = target.call(callData);
                              if (!success) {
                                  resultData.rrevert();
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./interfaces/IAuthorizableV06.sol";
                      import "./errors/LibRichErrorsV06.sol";
                      import "./errors/LibAuthorizableRichErrorsV06.sol";
                      import "./OwnableV06.sol";
                      // solhint-disable no-empty-blocks
                      contract AuthorizableV06 is
                          OwnableV06,
                          IAuthorizableV06
                      {
                          /// @dev Only authorized addresses can invoke functions with this modifier.
                          modifier onlyAuthorized {
                              _assertSenderIsAuthorized();
                              _;
                          }
                          // @dev Whether an address is authorized to call privileged functions.
                          // @param 0 Address to query.
                          // @return 0 Whether the address is authorized.
                          mapping (address => bool) public override authorized;
                          // @dev Whether an address is authorized to call privileged functions.
                          // @param 0 Index of authorized address.
                          // @return 0 Authorized address.
                          address[] public override authorities;
                          /// @dev Initializes the `owner` address.
                          constructor()
                              public
                              OwnableV06()
                          {}
                          /// @dev Authorizes an address.
                          /// @param target Address to authorize.
                          function addAuthorizedAddress(address target)
                              external
                              override
                              onlyOwner
                          {
                              _addAuthorizedAddress(target);
                          }
                          /// @dev Removes authorizion of an address.
                          /// @param target Address to remove authorization from.
                          function removeAuthorizedAddress(address target)
                              external
                              override
                              onlyOwner
                          {
                              if (!authorized[target]) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.TargetNotAuthorizedError(target));
                              }
                              for (uint256 i = 0; i < authorities.length; i++) {
                                  if (authorities[i] == target) {
                                      _removeAuthorizedAddressAtIndex(target, i);
                                      break;
                                  }
                              }
                          }
                          /// @dev Removes authorizion of an address.
                          /// @param target Address to remove authorization from.
                          /// @param index Index of target in authorities array.
                          function removeAuthorizedAddressAtIndex(
                              address target,
                              uint256 index
                          )
                              external
                              override
                              onlyOwner
                          {
                              _removeAuthorizedAddressAtIndex(target, index);
                          }
                          /// @dev Gets all authorized addresses.
                          /// @return Array of authorized addresses.
                          function getAuthorizedAddresses()
                              external
                              override
                              view
                              returns (address[] memory)
                          {
                              return authorities;
                          }
                          /// @dev Reverts if msg.sender is not authorized.
                          function _assertSenderIsAuthorized()
                              internal
                              view
                          {
                              if (!authorized[msg.sender]) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.SenderNotAuthorizedError(msg.sender));
                              }
                          }
                          /// @dev Authorizes an address.
                          /// @param target Address to authorize.
                          function _addAuthorizedAddress(address target)
                              internal
                          {
                              // Ensure that the target is not the zero address.
                              if (target == address(0)) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.ZeroCantBeAuthorizedError());
                              }
                              // Ensure that the target is not already authorized.
                              if (authorized[target]) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.TargetAlreadyAuthorizedError(target));
                              }
                              authorized[target] = true;
                              authorities.push(target);
                              emit AuthorizedAddressAdded(target, msg.sender);
                          }
                          /// @dev Removes authorizion of an address.
                          /// @param target Address to remove authorization from.
                          /// @param index Index of target in authorities array.
                          function _removeAuthorizedAddressAtIndex(
                              address target,
                              uint256 index
                          )
                              internal
                          {
                              if (!authorized[target]) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.TargetNotAuthorizedError(target));
                              }
                              if (index >= authorities.length) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.IndexOutOfBoundsError(
                                      index,
                                      authorities.length
                                  ));
                              }
                              if (authorities[index] != target) {
                                  LibRichErrorsV06.rrevert(LibAuthorizableRichErrorsV06.AuthorizedAddressMismatchError(
                                      authorities[index],
                                      target
                                  ));
                              }
                              delete authorized[target];
                              authorities[index] = authorities[authorities.length - 1];
                              authorities.pop();
                              emit AuthorizedAddressRemoved(target, msg.sender);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./IOwnableV06.sol";
                      interface IAuthorizableV06 is
                          IOwnableV06
                      {
                          // Event logged when a new address is authorized.
                          event AuthorizedAddressAdded(
                              address indexed target,
                              address indexed caller
                          );
                          // Event logged when a currently authorized address is unauthorized.
                          event AuthorizedAddressRemoved(
                              address indexed target,
                              address indexed caller
                          );
                          /// @dev Authorizes an address.
                          /// @param target Address to authorize.
                          function addAuthorizedAddress(address target)
                              external;
                          /// @dev Removes authorizion of an address.
                          /// @param target Address to remove authorization from.
                          function removeAuthorizedAddress(address target)
                              external;
                          /// @dev Removes authorizion of an address.
                          /// @param target Address to remove authorization from.
                          /// @param index Index of target in authorities array.
                          function removeAuthorizedAddressAtIndex(
                              address target,
                              uint256 index
                          )
                              external;
                          /// @dev Gets all authorized addresses.
                          /// @return authorizedAddresses Array of authorized addresses.
                          function getAuthorizedAddresses()
                              external
                              view
                              returns (address[] memory authorizedAddresses);
                          /// @dev Whether an adderss is authorized to call privileged functions.
                          /// @param addr Address to query.
                          /// @return isAuthorized Whether the address is authorized.
                          function authorized(address addr) external view returns (bool isAuthorized);
                          /// @dev All addresseses authorized to call privileged functions.
                          /// @param idx Index of authorized address.
                          /// @return addr Authorized address.
                          function authorities(uint256 idx) external view returns (address addr);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      interface IOwnableV06 {
                          /// @dev Emitted by Ownable when ownership is transferred.
                          /// @param previousOwner The previous owner of the contract.
                          /// @param newOwner The new owner of the contract.
                          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                          /// @dev Transfers ownership of the contract to a new address.
                          /// @param newOwner The address that will become the owner.
                          function transferOwnership(address newOwner) external;
                          /// @dev The owner of this contract.
                          /// @return ownerAddress The owner address.
                          function owner() external view returns (address ownerAddress);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibAuthorizableRichErrorsV06 {
                          // bytes4(keccak256("AuthorizedAddressMismatchError(address,address)"))
                          bytes4 internal constant AUTHORIZED_ADDRESS_MISMATCH_ERROR_SELECTOR =
                              0x140a84db;
                          // bytes4(keccak256("IndexOutOfBoundsError(uint256,uint256)"))
                          bytes4 internal constant INDEX_OUT_OF_BOUNDS_ERROR_SELECTOR =
                              0xe9f83771;
                          // bytes4(keccak256("SenderNotAuthorizedError(address)"))
                          bytes4 internal constant SENDER_NOT_AUTHORIZED_ERROR_SELECTOR =
                              0xb65a25b9;
                          // bytes4(keccak256("TargetAlreadyAuthorizedError(address)"))
                          bytes4 internal constant TARGET_ALREADY_AUTHORIZED_ERROR_SELECTOR =
                              0xde16f1a0;
                          // bytes4(keccak256("TargetNotAuthorizedError(address)"))
                          bytes4 internal constant TARGET_NOT_AUTHORIZED_ERROR_SELECTOR =
                              0xeb5108a2;
                          // bytes4(keccak256("ZeroCantBeAuthorizedError()"))
                          bytes internal constant ZERO_CANT_BE_AUTHORIZED_ERROR_BYTES =
                              hex"57654fe4";
                          // solhint-disable func-name-mixedcase
                          function AuthorizedAddressMismatchError(
                              address authorized,
                              address target
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  AUTHORIZED_ADDRESS_MISMATCH_ERROR_SELECTOR,
                                  authorized,
                                  target
                              );
                          }
                          function IndexOutOfBoundsError(
                              uint256 index,
                              uint256 length
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  INDEX_OUT_OF_BOUNDS_ERROR_SELECTOR,
                                  index,
                                  length
                              );
                          }
                          function SenderNotAuthorizedError(address sender)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  SENDER_NOT_AUTHORIZED_ERROR_SELECTOR,
                                  sender
                              );
                          }
                          function TargetAlreadyAuthorizedError(address target)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  TARGET_ALREADY_AUTHORIZED_ERROR_SELECTOR,
                                  target
                              );
                          }
                          function TargetNotAuthorizedError(address target)
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  TARGET_NOT_AUTHORIZED_ERROR_SELECTOR,
                                  target
                              );
                          }
                          function ZeroCantBeAuthorizedError()
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return ZERO_CANT_BE_AUTHORIZED_ERROR_BYTES;
                          }
                      }
                      /*
                        Copyright 2019 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./interfaces/IOwnableV06.sol";
                      import "./errors/LibRichErrorsV06.sol";
                      import "./errors/LibOwnableRichErrorsV06.sol";
                      contract OwnableV06 is
                          IOwnableV06
                      {
                          /// @dev The owner of this contract.
                          /// @return 0 The owner address.
                          address public override owner;
                          constructor() public {
                              owner = msg.sender;
                          }
                          modifier onlyOwner() {
                              _assertSenderIsOwner();
                              _;
                          }
                          /// @dev Change the owner of this contract.
                          /// @param newOwner New owner address.
                          function transferOwnership(address newOwner)
                              public
                              override
                              onlyOwner
                          {
                              if (newOwner == address(0)) {
                                  LibRichErrorsV06.rrevert(LibOwnableRichErrorsV06.TransferOwnerToZeroError());
                              } else {
                                  owner = newOwner;
                                  emit OwnershipTransferred(msg.sender, newOwner);
                              }
                          }
                          function _assertSenderIsOwner()
                              internal
                              view
                          {
                              if (msg.sender != owner) {
                                  LibRichErrorsV06.rrevert(LibOwnableRichErrorsV06.OnlyOwnerError(
                                      msg.sender,
                                      owner
                                  ));
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibOwnableRichErrorsV06 {
                          // bytes4(keccak256("OnlyOwnerError(address,address)"))
                          bytes4 internal constant ONLY_OWNER_ERROR_SELECTOR =
                              0x1de45ad1;
                          // bytes4(keccak256("TransferOwnerToZeroError()"))
                          bytes internal constant TRANSFER_OWNER_TO_ZERO_ERROR_BYTES =
                              hex"e69edc3e";
                          // solhint-disable func-name-mixedcase
                          function OnlyOwnerError(
                              address sender,
                              address owner
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  ONLY_OWNER_ERROR_SELECTOR,
                                  sender,
                                  owner
                              );
                          }
                          function TransferOwnerToZeroError()
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return TRANSFER_OWNER_TO_ZERO_ERROR_BYTES;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/interfaces/IAuthorizableV06.sol";
                      /// @dev The allowance target for the TokenSpender feature.
                      interface IAllowanceTarget is
                          IAuthorizableV06
                      {
                          /// @dev Execute an arbitrary call. Only an authority can call this.
                          /// @param target The call target.
                          /// @param callData The call data.
                          /// @return resultData The data returned by the call.
                          function executeCall(
                              address payable target,
                              bytes calldata callData
                          )
                              external
                              returns (bytes memory resultData);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibOwnableRichErrorsV06.sol";
                      import "../errors/LibWalletRichErrors.sol";
                      import "./IFlashWallet.sol";
                      /// @dev A contract that can execute arbitrary calls from its owner.
                      contract FlashWallet is
                          IFlashWallet
                      {
                          // solhint-disable no-unused-vars,indent,no-empty-blocks
                          using LibRichErrorsV06 for bytes;
                          // solhint-disable
                          /// @dev Store the owner/deployer as an immutable to make this contract stateless.
                          address public override immutable owner;
                          // solhint-enable
                          constructor() public {
                              // The deployer is the owner.
                              owner = msg.sender;
                          }
                          /// @dev Allows only the (immutable) owner to call a function.
                          modifier onlyOwner() virtual {
                              if (msg.sender != owner) {
                                  LibOwnableRichErrorsV06.OnlyOwnerError(
                                      msg.sender,
                                      owner
                                  ).rrevert();
                              }
                              _;
                          }
                          /// @dev Execute an arbitrary call. Only an authority can call this.
                          /// @param target The call target.
                          /// @param callData The call data.
                          /// @param value Ether to attach to the call.
                          /// @return resultData The data returned by the call.
                          function executeCall(
                              address payable target,
                              bytes calldata callData,
                              uint256 value
                          )
                              external
                              payable
                              override
                              onlyOwner
                              returns (bytes memory resultData)
                          {
                              bool success;
                              (success, resultData) = target.call{value: value}(callData);
                              if (!success) {
                                  LibWalletRichErrors
                                      .WalletExecuteCallFailedError(
                                          address(this),
                                          target,
                                          callData,
                                          value,
                                          resultData
                                      )
                                      .rrevert();
                              }
                          }
                          /// @dev Execute an arbitrary delegatecall, in the context of this puppet.
                          ///      Only an authority can call this.
                          /// @param target The call target.
                          /// @param callData The call data.
                          /// @return resultData The data returned by the call.
                          function executeDelegateCall(
                              address payable target,
                              bytes calldata callData
                          )
                              external
                              payable
                              override
                              onlyOwner
                              returns (bytes memory resultData)
                          {
                              bool success;
                              (success, resultData) = target.delegatecall(callData);
                              if (!success) {
                                  LibWalletRichErrors
                                      .WalletExecuteDelegateCallFailedError(
                                          address(this),
                                          target,
                                          callData,
                                          resultData
                                      )
                                      .rrevert();
                              }
                          }
                          // solhint-disable
                          /// @dev Allows this contract to receive ether.
                          receive() external override payable {}
                          // solhint-enable
                          /// @dev Signal support for receiving ERC1155 tokens.
                          /// @param interfaceID The interface ID, as per ERC-165 rules.
                          /// @return hasSupport `true` if this contract supports an ERC-165 interface.
                          function supportsInterface(bytes4 interfaceID)
                              external
                              pure
                              returns (bool hasSupport)
                          {
                              return  interfaceID == this.supportsInterface.selector ||
                                      interfaceID == this.onERC1155Received.selector ^ this.onERC1155BatchReceived.selector ||
                                      interfaceID == this.tokenFallback.selector;
                          }
                          ///  @dev Allow this contract to receive ERC1155 tokens.
                          ///  @return success  `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
                          function onERC1155Received(
                              address, // operator,
                              address, // from,
                              uint256, // id,
                              uint256, // value,
                              bytes calldata //data
                          )
                              external
                              pure
                              returns (bytes4 success)
                          {
                              return this.onERC1155Received.selector;
                          }
                          ///  @dev Allow this contract to receive ERC1155 tokens.
                          ///  @return success  `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
                          function onERC1155BatchReceived(
                              address, // operator,
                              address, // from,
                              uint256[] calldata, // ids,
                              uint256[] calldata, // values,
                              bytes calldata // data
                          )
                              external
                              pure
                              returns (bytes4 success)
                          {
                              return this.onERC1155BatchReceived.selector;
                          }
                          /// @dev Allows this contract to receive ERC223 tokens.
                          function tokenFallback(
                              address, // from,
                              uint256, // value,
                              bytes calldata // value
                          )
                              external
                              pure
                          {}
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/interfaces/IOwnableV06.sol";
                      /// @dev A contract that can execute arbitrary calls from its owner.
                      interface IFlashWallet {
                          /// @dev Execute an arbitrary call. Only an authority can call this.
                          /// @param target The call target.
                          /// @param callData The call data.
                          /// @param value Ether to attach to the call.
                          /// @return resultData The data returned by the call.
                          function executeCall(
                              address payable target,
                              bytes calldata callData,
                              uint256 value
                          )
                              external
                              payable
                              returns (bytes memory resultData);
                          /// @dev Execute an arbitrary delegatecall, in the context of this puppet.
                          ///      Only an authority can call this.
                          /// @param target The call target.
                          /// @param callData The call data.
                          /// @return resultData The data returned by the call.
                          function executeDelegateCall(
                              address payable target,
                              bytes calldata callData
                          )
                              external
                              payable
                              returns (bytes memory resultData);
                          /// @dev Allows the puppet to receive ETH.
                          receive() external payable;
                          /// @dev Fetch the immutable owner/deployer of this contract.
                          /// @return owner_ The immutable owner/deployer/
                          function owner() external view returns (address owner_);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/AuthorizableV06.sol";
                      /// @dev A contract with a `die()` function.
                      interface IKillable {
                          function die() external;
                      }
                      /// @dev Deployer contract for ERC20 transformers.
                      ///      Only authorities may call `deploy()` and `kill()`.
                      contract TransformerDeployer is
                          AuthorizableV06
                      {
                          /// @dev Emitted when a contract is deployed via `deploy()`.
                          /// @param deployedAddress The address of the deployed contract.
                          /// @param nonce The deployment nonce.
                          /// @param sender The caller of `deploy()`.
                          event Deployed(address deployedAddress, uint256 nonce, address sender);
                          /// @dev Emitted when a contract is killed via `kill()`.
                          /// @param target The address of the contract being killed..
                          /// @param sender The caller of `kill()`.
                          event Killed(address target, address sender);
                          // @dev The current nonce of this contract.
                          uint256 public nonce = 1;
                          // @dev Mapping of deployed contract address to deployment nonce.
                          mapping (address => uint256) public toDeploymentNonce;
                          /// @dev Create this contract and register authorities.
                          constructor(address[] memory authorities) public {
                              for (uint256 i = 0; i < authorities.length; ++i) {
                                  _addAuthorizedAddress(authorities[i]);
                              }
                          }
                          /// @dev Deploy a new contract. Only callable by an authority.
                          ///      Any attached ETH will also be forwarded.
                          function deploy(bytes memory bytecode)
                              public
                              payable
                              onlyAuthorized
                              returns (address deployedAddress)
                          {
                              uint256 deploymentNonce = nonce;
                              nonce += 1;
                              assembly {
                                  deployedAddress := create(callvalue(), add(bytecode, 32), mload(bytecode))
                              }
                              toDeploymentNonce[deployedAddress] = deploymentNonce;
                              emit Deployed(deployedAddress, deploymentNonce, msg.sender);
                          }
                          /// @dev Call `die()` on a contract. Only callable by an authority.
                          function kill(IKillable target)
                              public
                              onlyAuthorized
                          {
                              target.die();
                              emit Killed(address(target), msg.sender);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      /// @dev Basic interface for a feature contract.
                      interface IFeature {
                          // solhint-disable func-name-mixedcase
                          /// @dev The name of this feature set.
                          function FEATURE_NAME() external view returns (string memory name);
                          /// @dev The version of this feature set.
                          function FEATURE_VERSION() external view returns (uint256 version);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/interfaces/IOwnableV06.sol";
                      // solhint-disable no-empty-blocks
                      /// @dev Owner management and migration features.
                      interface IOwnable is
                          IOwnableV06
                      {
                          /// @dev Emitted when `migrate()` is called.
                          /// @param caller The caller of `migrate()`.
                          /// @param migrator The migration contract.
                          /// @param newOwner The address of the new owner.
                          event Migrated(address caller, address migrator, address newOwner);
                          /// @dev Execute a migration function in the context of the ZeroEx contract.
                          ///      The result of the function being called should be the magic bytes
                          ///      0x2c64c5ef (`keccack('MIGRATE_SUCCESS')`). Only callable by the owner.
                          ///      The owner will be temporarily set to `address(this)` inside the call.
                          ///      Before returning, the owner will be set to `newOwner`.
                          /// @param target The migrator contract address.
                          /// @param newOwner The address of the new owner.
                          /// @param data The call data.
                          function migrate(address target, bytes calldata data, address newOwner) external;
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      /// @dev Basic registry management features.
                      interface ISimpleFunctionRegistry {
                          /// @dev A function implementation was updated via `extend()` or `rollback()`.
                          /// @param selector The function selector.
                          /// @param oldImpl The implementation contract address being replaced.
                          /// @param newImpl The replacement implementation contract address.
                          event ProxyFunctionUpdated(bytes4 indexed selector, address oldImpl, address newImpl);
                          /// @dev Roll back to a prior implementation of a function.
                          /// @param selector The function selector.
                          /// @param targetImpl The address of an older implementation of the function.
                          function rollback(bytes4 selector, address targetImpl) external;
                          /// @dev Register or replace a function.
                          /// @param selector The function selector.
                          /// @param impl The implementation contract for the function.
                          function extend(bytes4 selector, address impl) external;
                          /// @dev Retrieve the length of the rollback history for a function.
                          /// @param selector The function selector.
                          /// @return rollbackLength The number of items in the rollback history for
                          ///         the function.
                          function getRollbackLength(bytes4 selector)
                              external
                              view
                              returns (uint256 rollbackLength);
                          /// @dev Retrieve an entry in the rollback history for a function.
                          /// @param selector The function selector.
                          /// @param idx The index in the rollback history.
                          /// @return impl An implementation address for the function at
                          ///         index `idx`.
                          function getRollbackEntryAtIndex(bytes4 selector, uint256 idx)
                              external
                              view
                              returns (address impl);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      /// @dev Feature that allows spending token allowances.
                      interface ITokenSpender {
                          /// @dev Transfers ERC20 tokens from `owner` to `to`.
                          ///      Only callable from within.
                          /// @param token The token to spend.
                          /// @param owner The owner of the tokens.
                          /// @param to The recipient of the tokens.
                          /// @param amount The amount of `token` to transfer.
                          function _spendERC20Tokens(
                              IERC20TokenV06 token,
                              address owner,
                              address to,
                              uint256 amount
                          )
                              external;
                          /// @dev Gets the maximum amount of an ERC20 token `token` that can be
                          ///      pulled from `owner`.
                          /// @param token The token to spend.
                          /// @param owner The owner of the tokens.
                          /// @return amount The amount of tokens that can be pulled.
                          function getSpendableERC20BalanceOf(IERC20TokenV06 token, address owner)
                              external
                              view
                              returns (uint256 amount);
                          /// @dev Get the address of the allowance target.
                          /// @return target The target of token allowances.
                          function getAllowanceTarget() external view returns (address target);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      interface IERC20TokenV06 {
                          // solhint-disable no-simple-event-func-name
                          event Transfer(
                              address indexed from,
                              address indexed to,
                              uint256 value
                          );
                          event Approval(
                              address indexed owner,
                              address indexed spender,
                              uint256 value
                          );
                          /// @dev send `value` token to `to` from `msg.sender`
                          /// @param to The address of the recipient
                          /// @param value The amount of token to be transferred
                          /// @return True if transfer was successful
                          function transfer(address to, uint256 value)
                              external
                              returns (bool);
                          /// @dev send `value` token to `to` from `from` on the condition it is approved by `from`
                          /// @param from The address of the sender
                          /// @param to The address of the recipient
                          /// @param value The amount of token to be transferred
                          /// @return True if transfer was successful
                          function transferFrom(
                              address from,
                              address to,
                              uint256 value
                          )
                              external
                              returns (bool);
                          /// @dev `msg.sender` approves `spender` to spend `value` tokens
                          /// @param spender The address of the account able to transfer the tokens
                          /// @param value The amount of wei to be approved for transfer
                          /// @return Always true if the call has enough gas to complete execution
                          function approve(address spender, uint256 value)
                              external
                              returns (bool);
                          /// @dev Query total supply of token
                          /// @return Total supply of token
                          function totalSupply()
                              external
                              view
                              returns (uint256);
                          /// @dev Get the balance of `owner`.
                          /// @param owner The address from which the balance will be retrieved
                          /// @return Balance of owner
                          function balanceOf(address owner)
                              external
                              view
                              returns (uint256);
                          /// @dev Get the allowance for `spender` to spend from `owner`.
                          /// @param owner The address of the account owning tokens
                          /// @param spender The address of the account able to transfer the tokens
                          /// @return Amount of remaining tokens allowed to spent
                          function allowance(address owner, address spender)
                              external
                              view
                              returns (uint256);
                          /// @dev Get the number of decimals this token has.
                          function decimals()
                              external
                              view
                              returns (uint8);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "../transformers/IERC20Transformer.sol";
                      import "../external/IFlashWallet.sol";
                      /// @dev Feature to composably transform between ERC20 tokens.
                      interface ITransformERC20 {
                          /// @dev Defines a transformation to run in `transformERC20()`.
                          struct Transformation {
                              // The deployment nonce for the transformer.
                              // The address of the transformer contract will be derived from this
                              // value.
                              uint32 deploymentNonce;
                              // Arbitrary data to pass to the transformer.
                              bytes data;
                          }
                          /// @dev Raised upon a successful `transformERC20`.
                          /// @param taker The taker (caller) address.
                          /// @param inputToken The token being provided by the taker.
                          ///        If `0xeee...`, ETH is implied and should be provided with the call.`
                          /// @param outputToken The token to be acquired by the taker.
                          ///        `0xeee...` implies ETH.
                          /// @param inputTokenAmount The amount of `inputToken` to take from the taker.
                          /// @param outputTokenAmount The amount of `outputToken` received by the taker.
                          event TransformedERC20(
                              address indexed taker,
                              address inputToken,
                              address outputToken,
                              uint256 inputTokenAmount,
                              uint256 outputTokenAmount
                          );
                          /// @dev Raised when `setTransformerDeployer()` is called.
                          /// @param transformerDeployer The new deployer address.
                          event TransformerDeployerUpdated(address transformerDeployer);
                          /// @dev Replace the allowed deployer for transformers.
                          ///      Only callable by the owner.
                          /// @param transformerDeployer The address of the trusted deployer for transformers.
                          function setTransformerDeployer(address transformerDeployer)
                              external;
                          /// @dev Deploy a new flash wallet instance and replace the current one with it.
                          ///      Useful if we somehow break the current wallet instance.
                          ///      Anyone can call this.
                          /// @return wallet The new wallet instance.
                          function createTransformWallet()
                              external
                              returns (IFlashWallet wallet);
                          /// @dev Executes a series of transformations to convert an ERC20 `inputToken`
                          ///      to an ERC20 `outputToken`.
                          /// @param inputToken The token being provided by the sender.
                          ///        If `0xeee...`, ETH is implied and should be provided with the call.`
                          /// @param outputToken The token to be acquired by the sender.
                          ///        `0xeee...` implies ETH.
                          /// @param inputTokenAmount The amount of `inputToken` to take from the sender.
                          /// @param minOutputTokenAmount The minimum amount of `outputToken` the sender
                          ///        must receive for the entire transformation to succeed.
                          /// @param transformations The transformations to execute on the token balance(s)
                          ///        in sequence.
                          /// @return outputTokenAmount The amount of `outputToken` received by the sender.
                          function transformERC20(
                              IERC20TokenV06 inputToken,
                              IERC20TokenV06 outputToken,
                              uint256 inputTokenAmount,
                              uint256 minOutputTokenAmount,
                              Transformation[] calldata transformations
                          )
                              external
                              payable
                              returns (uint256 outputTokenAmount);
                          /// @dev Internal version of `transformERC20()`. Only callable from within.
                          /// @param callDataHash Hash of the ingress calldata.
                          /// @param taker The taker address.
                          /// @param inputToken The token being provided by the taker.
                          ///        If `0xeee...`, ETH is implied and should be provided with the call.`
                          /// @param outputToken The token to be acquired by the taker.
                          ///        `0xeee...` implies ETH.
                          /// @param inputTokenAmount The amount of `inputToken` to take from the taker.
                          /// @param minOutputTokenAmount The minimum amount of `outputToken` the taker
                          ///        must receive for the entire transformation to succeed.
                          /// @param transformations The transformations to execute on the token balance(s)
                          ///        in sequence.
                          /// @return outputTokenAmount The amount of `outputToken` received by the taker.
                          function _transformERC20(
                              bytes32 callDataHash,
                              address payable taker,
                              IERC20TokenV06 inputToken,
                              IERC20TokenV06 outputToken,
                              uint256 inputTokenAmount,
                              uint256 minOutputTokenAmount,
                              Transformation[] calldata transformations
                          )
                              external
                              payable
                              returns (uint256 outputTokenAmount);
                          /// @dev Return the current wallet instance that will serve as the execution
                          ///      context for transformations.
                          /// @return wallet The wallet instance.
                          function getTransformWallet()
                              external
                              view
                              returns (IFlashWallet wallet);
                          /// @dev Return the allowed deployer for transformers.
                          /// @return deployer The transform deployer address.
                          function getTransformerDeployer()
                              external
                              view
                              returns (address deployer);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      /// @dev A transformation callback used in `TransformERC20.transformERC20()`.
                      interface IERC20Transformer {
                          /// @dev Called from `TransformERC20.transformERC20()`. This will be
                          ///      delegatecalled in the context of the FlashWallet instance being used.
                          /// @param callDataHash The hash of the `TransformERC20.transformERC20()` calldata.
                          /// @param taker The taker address (caller of `TransformERC20.transformERC20()`).
                          /// @param data Arbitrary data to pass to the transformer.
                          /// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
                          function transform(
                              bytes32 callDataHash,
                              address payable taker,
                              bytes calldata data
                          )
                              external
                              returns (bytes4 success);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../fixins/FixinCommon.sol";
                      import "../errors/LibOwnableRichErrors.sol";
                      import "../storage/LibOwnableStorage.sol";
                      import "../migrations/LibBootstrap.sol";
                      import "../migrations/LibMigrate.sol";
                      import "./IFeature.sol";
                      import "./IOwnable.sol";
                      import "./SimpleFunctionRegistry.sol";
                      /// @dev Owner management features.
                      contract Ownable is
                          IFeature,
                          IOwnable,
                          FixinCommon
                      {
                          // solhint-disable
                          /// @dev Name of this feature.
                          string public constant override FEATURE_NAME = "Ownable";
                          /// @dev Version of this feature.
                          uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
                          /// @dev The deployed address of this contract.
                          address immutable private _implementation;
                          // solhint-enable
                          using LibRichErrorsV06 for bytes;
                          constructor() public {
                              _implementation = address(this);
                          }
                          /// @dev Initializes this feature. The intial owner will be set to this (ZeroEx)
                          ///      to allow the bootstrappers to call `extend()`. Ownership should be
                          ///      transferred to the real owner by the bootstrapper after
                          ///      bootstrapping is complete.
                          /// @return success Magic bytes if successful.
                          function bootstrap() external returns (bytes4 success) {
                              // Set the owner to ourselves to allow bootstrappers to call `extend()`.
                              LibOwnableStorage.getStorage().owner = address(this);
                              // Register feature functions.
                              SimpleFunctionRegistry(address(this))._extendSelf(this.transferOwnership.selector, _implementation);
                              SimpleFunctionRegistry(address(this))._extendSelf(this.owner.selector, _implementation);
                              SimpleFunctionRegistry(address(this))._extendSelf(this.migrate.selector, _implementation);
                              return LibBootstrap.BOOTSTRAP_SUCCESS;
                          }
                          /// @dev Change the owner of this contract.
                          ///      Only directly callable by the owner.
                          /// @param newOwner New owner address.
                          function transferOwnership(address newOwner)
                              external
                              override
                              onlyOwner
                          {
                              LibOwnableStorage.Storage storage proxyStor = LibOwnableStorage.getStorage();
                              if (newOwner == address(0)) {
                                  LibOwnableRichErrors.TransferOwnerToZeroError().rrevert();
                              } else {
                                  proxyStor.owner = newOwner;
                                  emit OwnershipTransferred(msg.sender, newOwner);
                              }
                          }
                          /// @dev Execute a migration function in the context of the ZeroEx contract.
                          ///      The result of the function being called should be the magic bytes
                          ///      0x2c64c5ef (`keccack('MIGRATE_SUCCESS')`). Only callable by the owner.
                          ///      Temporarily sets the owner to ourselves so we can perform admin functions.
                          ///      Before returning, the owner will be set to `newOwner`.
                          /// @param target The migrator contract address.
                          /// @param data The call data.
                          /// @param newOwner The address of the new owner.
                          function migrate(address target, bytes calldata data, address newOwner)
                              external
                              override
                              onlyOwner
                          {
                              if (newOwner == address(0)) {
                                  LibOwnableRichErrors.TransferOwnerToZeroError().rrevert();
                              }
                              LibOwnableStorage.Storage storage stor = LibOwnableStorage.getStorage();
                              // The owner will be temporarily set to `address(this)` inside the call.
                              stor.owner = address(this);
                              // Perform the migration.
                              LibMigrate.delegatecallMigrateFunction(target, data);
                              // Update the owner.
                              stor.owner = newOwner;
                              emit Migrated(msg.sender, target, newOwner);
                          }
                          /// @dev Get the owner of this contract.
                          /// @return owner_ The owner of this contract.
                          function owner() external override view returns (address owner_) {
                              return LibOwnableStorage.getStorage().owner;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../errors/LibCommonRichErrors.sol";
                      import "../errors/LibOwnableRichErrors.sol";
                      import "../features/IOwnable.sol";
                      /// @dev Common feature utilities.
                      contract FixinCommon {
                          using LibRichErrorsV06 for bytes;
                          /// @dev The caller must be this contract.
                          modifier onlySelf() virtual {
                              if (msg.sender != address(this)) {
                                  LibCommonRichErrors.OnlyCallableBySelfError(msg.sender).rrevert();
                              }
                              _;
                          }
                          /// @dev The caller of this function must be the owner.
                          modifier onlyOwner() virtual {
                              {
                                  address owner = IOwnable(address(this)).owner();
                                  if (msg.sender != owner) {
                                      LibOwnableRichErrors.OnlyOwnerError(
                                          msg.sender,
                                          owner
                                      ).rrevert();
                                  }
                              }
                              _;
                          }
                          /// @dev Encode a feature version as a `uint256`.
                          /// @param major The major version number of the feature.
                          /// @param minor The minor version number of the feature.
                          /// @param revision The revision number of the feature.
                          /// @return encodedVersion The encoded version number.
                          function _encodeVersion(uint32 major, uint32 minor, uint32 revision)
                              internal
                              pure
                              returns (uint256 encodedVersion)
                          {
                              return (major << 64) | (minor << 32) | revision;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./LibStorage.sol";
                      /// @dev Storage helpers for the `Ownable` feature.
                      library LibOwnableStorage {
                          /// @dev Storage bucket for this feature.
                          struct Storage {
                              // The owner of this contract.
                              address owner;
                          }
                          /// @dev Get the storage bucket for this contract.
                          function getStorage() internal pure returns (Storage storage stor) {
                              uint256 storageSlot = LibStorage.getStorageSlot(
                                  LibStorage.StorageId.Ownable
                              );
                              // Dip into assembly to change the slot pointed to by the local
                              // variable `stor`.
                              // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
                              assembly { stor_slot := storageSlot }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../errors/LibOwnableRichErrors.sol";
                      library LibMigrate {
                          /// @dev Magic bytes returned by a migrator to indicate success.
                          ///      This is `keccack('MIGRATE_SUCCESS')`.
                          bytes4 internal constant MIGRATE_SUCCESS = 0x2c64c5ef;
                          using LibRichErrorsV06 for bytes;
                          /// @dev Perform a delegatecall and ensure it returns the magic bytes.
                          /// @param target The call target.
                          /// @param data The call data.
                          function delegatecallMigrateFunction(
                              address target,
                              bytes memory data
                          )
                              internal
                          {
                              (bool success, bytes memory resultData) = target.delegatecall(data);
                              if (!success ||
                                  resultData.length != 32 ||
                                  abi.decode(resultData, (bytes4)) != MIGRATE_SUCCESS)
                              {
                                  LibOwnableRichErrors.MigrateCallFailedError(target, resultData).rrevert();
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../fixins/FixinCommon.sol";
                      import "../storage/LibProxyStorage.sol";
                      import "../storage/LibSimpleFunctionRegistryStorage.sol";
                      import "../errors/LibSimpleFunctionRegistryRichErrors.sol";
                      import "../migrations/LibBootstrap.sol";
                      import "./IFeature.sol";
                      import "./ISimpleFunctionRegistry.sol";
                      /// @dev Basic registry management features.
                      contract SimpleFunctionRegistry is
                          IFeature,
                          ISimpleFunctionRegistry,
                          FixinCommon
                      {
                          // solhint-disable
                          /// @dev Name of this feature.
                          string public constant override FEATURE_NAME = "SimpleFunctionRegistry";
                          /// @dev Version of this feature.
                          uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
                          /// @dev The deployed address of this contract.
                          address private immutable _implementation;
                          // solhint-enable
                          using LibRichErrorsV06 for bytes;
                          constructor() public {
                              _implementation = address(this);
                          }
                          /// @dev Initializes this feature, registering its own functions.
                          /// @return success Magic bytes if successful.
                          function bootstrap()
                              external
                              returns (bytes4 success)
                          {
                              // Register the registration functions (inception vibes).
                              _extend(this.extend.selector, _implementation);
                              _extend(this._extendSelf.selector, _implementation);
                              // Register the rollback function.
                              _extend(this.rollback.selector, _implementation);
                              // Register getters.
                              _extend(this.getRollbackLength.selector, _implementation);
                              _extend(this.getRollbackEntryAtIndex.selector, _implementation);
                              return LibBootstrap.BOOTSTRAP_SUCCESS;
                          }
                          /// @dev Roll back to a prior implementation of a function.
                          ///      Only directly callable by an authority.
                          /// @param selector The function selector.
                          /// @param targetImpl The address of an older implementation of the function.
                          function rollback(bytes4 selector, address targetImpl)
                              external
                              override
                              onlyOwner
                          {
                              (
                                  LibSimpleFunctionRegistryStorage.Storage storage stor,
                                  LibProxyStorage.Storage storage proxyStor
                              ) = _getStorages();
                              address currentImpl = proxyStor.impls[selector];
                              if (currentImpl == targetImpl) {
                                  // Do nothing if already at targetImpl.
                                  return;
                              }
                              // Walk history backwards until we find the target implementation.
                              address[] storage history = stor.implHistory[selector];
                              uint256 i = history.length;
                              for (; i > 0; --i) {
                                  address impl = history[i - 1];
                                  history.pop();
                                  if (impl == targetImpl) {
                                      break;
                                  }
                              }
                              if (i == 0) {
                                  LibSimpleFunctionRegistryRichErrors.NotInRollbackHistoryError(
                                      selector,
                                      targetImpl
                                  ).rrevert();
                              }
                              proxyStor.impls[selector] = targetImpl;
                              emit ProxyFunctionUpdated(selector, currentImpl, targetImpl);
                          }
                          /// @dev Register or replace a function.
                          ///      Only directly callable by an authority.
                          /// @param selector The function selector.
                          /// @param impl The implementation contract for the function.
                          function extend(bytes4 selector, address impl)
                              external
                              override
                              onlyOwner
                          {
                              _extend(selector, impl);
                          }
                          /// @dev Register or replace a function.
                          ///      Only callable from within.
                          ///      This function is only used during the bootstrap process and
                          ///      should be deregistered by the deployer after bootstrapping is
                          ///      complete.
                          /// @param selector The function selector.
                          /// @param impl The implementation contract for the function.
                          function _extendSelf(bytes4 selector, address impl)
                              external
                              onlySelf
                          {
                              _extend(selector, impl);
                          }
                          /// @dev Retrieve the length of the rollback history for a function.
                          /// @param selector The function selector.
                          /// @return rollbackLength The number of items in the rollback history for
                          ///         the function.
                          function getRollbackLength(bytes4 selector)
                              external
                              override
                              view
                              returns (uint256 rollbackLength)
                          {
                              return LibSimpleFunctionRegistryStorage.getStorage().implHistory[selector].length;
                          }
                          /// @dev Retrieve an entry in the rollback history for a function.
                          /// @param selector The function selector.
                          /// @param idx The index in the rollback history.
                          /// @return impl An implementation address for the function at
                          ///         index `idx`.
                          function getRollbackEntryAtIndex(bytes4 selector, uint256 idx)
                              external
                              override
                              view
                              returns (address impl)
                          {
                              return LibSimpleFunctionRegistryStorage.getStorage().implHistory[selector][idx];
                          }
                          /// @dev Register or replace a function.
                          /// @param selector The function selector.
                          /// @param impl The implementation contract for the function.
                          function _extend(bytes4 selector, address impl)
                              private
                          {
                              (
                                  LibSimpleFunctionRegistryStorage.Storage storage stor,
                                  LibProxyStorage.Storage storage proxyStor
                              ) = _getStorages();
                              address oldImpl = proxyStor.impls[selector];
                              address[] storage history = stor.implHistory[selector];
                              history.push(oldImpl);
                              proxyStor.impls[selector] = impl;
                              emit ProxyFunctionUpdated(selector, oldImpl, impl);
                          }
                          /// @dev Get the storage buckets for this feature and the proxy.
                          /// @return stor Storage bucket for this feature.
                          /// @return proxyStor age bucket for the proxy.
                          function _getStorages()
                              private
                              pure
                              returns (
                                  LibSimpleFunctionRegistryStorage.Storage storage stor,
                                  LibProxyStorage.Storage storage proxyStor
                              )
                          {
                              return (
                                  LibSimpleFunctionRegistryStorage.getStorage(),
                                  LibProxyStorage.getStorage()
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./LibStorage.sol";
                      /// @dev Storage helpers for the `SimpleFunctionRegistry` feature.
                      library LibSimpleFunctionRegistryStorage {
                          /// @dev Storage bucket for this feature.
                          struct Storage {
                              // Mapping of function selector -> implementation history.
                              mapping(bytes4 => address[]) implHistory;
                          }
                          /// @dev Get the storage bucket for this contract.
                          function getStorage() internal pure returns (Storage storage stor) {
                              uint256 storageSlot = LibStorage.getStorageSlot(
                                  LibStorage.StorageId.SimpleFunctionRegistry
                              );
                              // Dip into assembly to change the slot pointed to by the local
                              // variable `stor`.
                              // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
                              assembly { stor_slot := storageSlot }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
                      import "../errors/LibSpenderRichErrors.sol";
                      import "../fixins/FixinCommon.sol";
                      import "../migrations/LibMigrate.sol";
                      import "../external/IAllowanceTarget.sol";
                      import "../storage/LibTokenSpenderStorage.sol";
                      import "./ITokenSpender.sol";
                      import "./IFeature.sol";
                      import "./ISimpleFunctionRegistry.sol";
                      /// @dev Feature that allows spending token allowances.
                      contract TokenSpender is
                          IFeature,
                          ITokenSpender,
                          FixinCommon
                      {
                          // solhint-disable
                          /// @dev Name of this feature.
                          string public constant override FEATURE_NAME = "TokenSpender";
                          /// @dev Version of this feature.
                          uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
                          /// @dev The implementation address of this feature.
                          address private immutable _implementation;
                          // solhint-enable
                          using LibRichErrorsV06 for bytes;
                          constructor() public {
                              _implementation = address(this);
                          }
                          /// @dev Initialize and register this feature. Should be delegatecalled
                          ///      into during a `Migrate.migrate()`.
                          /// @param allowanceTarget An `allowanceTarget` instance, configured to have
                          ///        the ZeroeEx contract as an authority.
                          /// @return success `MIGRATE_SUCCESS` on success.
                          function migrate(IAllowanceTarget allowanceTarget) external returns (bytes4 success) {
                              LibTokenSpenderStorage.getStorage().allowanceTarget = allowanceTarget;
                              ISimpleFunctionRegistry(address(this))
                                  .extend(this.getAllowanceTarget.selector, _implementation);
                              ISimpleFunctionRegistry(address(this))
                                  .extend(this._spendERC20Tokens.selector, _implementation);
                              ISimpleFunctionRegistry(address(this))
                                  .extend(this.getSpendableERC20BalanceOf.selector, _implementation);
                              return LibMigrate.MIGRATE_SUCCESS;
                          }
                          /// @dev Transfers ERC20 tokens from `owner` to `to`. Only callable from within.
                          /// @param token The token to spend.
                          /// @param owner The owner of the tokens.
                          /// @param to The recipient of the tokens.
                          /// @param amount The amount of `token` to transfer.
                          function _spendERC20Tokens(
                              IERC20TokenV06 token,
                              address owner,
                              address to,
                              uint256 amount
                          )
                              external
                              override
                              onlySelf
                          {
                              IAllowanceTarget spender = LibTokenSpenderStorage.getStorage().allowanceTarget;
                              // Have the allowance target execute an ERC20 `transferFrom()`.
                              (bool didSucceed, bytes memory resultData) = address(spender).call(
                                  abi.encodeWithSelector(
                                      IAllowanceTarget.executeCall.selector,
                                      address(token),
                                      abi.encodeWithSelector(
                                          IERC20TokenV06.transferFrom.selector,
                                          owner,
                                          to,
                                          amount
                                      )
                                  )
                              );
                              if (didSucceed) {
                                  resultData = abi.decode(resultData, (bytes));
                              }
                              if (!didSucceed || !LibERC20TokenV06.isSuccessfulResult(resultData)) {
                                  LibSpenderRichErrors.SpenderERC20TransferFromFailedError(
                                      address(token),
                                      owner,
                                      to,
                                      amount,
                                      resultData
                                  ).rrevert();
                              }
                          }
                          /// @dev Gets the maximum amount of an ERC20 token `token` that can be
                          ///      pulled from `owner` by the token spender.
                          /// @param token The token to spend.
                          /// @param owner The owner of the tokens.
                          /// @return amount The amount of tokens that can be pulled.
                          function getSpendableERC20BalanceOf(IERC20TokenV06 token, address owner)
                              external
                              override
                              view
                              returns (uint256 amount)
                          {
                              return LibSafeMathV06.min256(
                                  token.allowance(owner, address(LibTokenSpenderStorage.getStorage().allowanceTarget)),
                                  token.balanceOf(owner)
                              );
                          }
                          /// @dev Get the address of the allowance target.
                          /// @return target The target of token allowances.
                          function getAllowanceTarget()
                              external
                              override
                              view
                              returns (address target)
                          {
                              return address(LibTokenSpenderStorage.getStorage().allowanceTarget);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./errors/LibRichErrorsV06.sol";
                      import "./errors/LibSafeMathRichErrorsV06.sol";
                      library LibSafeMathV06 {
                          function safeMul(uint256 a, uint256 b)
                              internal
                              pure
                              returns (uint256)
                          {
                              if (a == 0) {
                                  return 0;
                              }
                              uint256 c = a * b;
                              if (c / a != b) {
                                  LibRichErrorsV06.rrevert(LibSafeMathRichErrorsV06.Uint256BinOpError(
                                      LibSafeMathRichErrorsV06.BinOpErrorCodes.MULTIPLICATION_OVERFLOW,
                                      a,
                                      b
                                  ));
                              }
                              return c;
                          }
                          function safeDiv(uint256 a, uint256 b)
                              internal
                              pure
                              returns (uint256)
                          {
                              if (b == 0) {
                                  LibRichErrorsV06.rrevert(LibSafeMathRichErrorsV06.Uint256BinOpError(
                                      LibSafeMathRichErrorsV06.BinOpErrorCodes.DIVISION_BY_ZERO,
                                      a,
                                      b
                                  ));
                              }
                              uint256 c = a / b;
                              return c;
                          }
                          function safeSub(uint256 a, uint256 b)
                              internal
                              pure
                              returns (uint256)
                          {
                              if (b > a) {
                                  LibRichErrorsV06.rrevert(LibSafeMathRichErrorsV06.Uint256BinOpError(
                                      LibSafeMathRichErrorsV06.BinOpErrorCodes.SUBTRACTION_UNDERFLOW,
                                      a,
                                      b
                                  ));
                              }
                              return a - b;
                          }
                          function safeAdd(uint256 a, uint256 b)
                              internal
                              pure
                              returns (uint256)
                          {
                              uint256 c = a + b;
                              if (c < a) {
                                  LibRichErrorsV06.rrevert(LibSafeMathRichErrorsV06.Uint256BinOpError(
                                      LibSafeMathRichErrorsV06.BinOpErrorCodes.ADDITION_OVERFLOW,
                                      a,
                                      b
                                  ));
                              }
                              return c;
                          }
                          function max256(uint256 a, uint256 b)
                              internal
                              pure
                              returns (uint256)
                          {
                              return a >= b ? a : b;
                          }
                          function min256(uint256 a, uint256 b)
                              internal
                              pure
                              returns (uint256)
                          {
                              return a < b ? a : b;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibSafeMathRichErrorsV06 {
                          // bytes4(keccak256("Uint256BinOpError(uint8,uint256,uint256)"))
                          bytes4 internal constant UINT256_BINOP_ERROR_SELECTOR =
                              0xe946c1bb;
                          // bytes4(keccak256("Uint256DowncastError(uint8,uint256)"))
                          bytes4 internal constant UINT256_DOWNCAST_ERROR_SELECTOR =
                              0xc996af7b;
                          enum BinOpErrorCodes {
                              ADDITION_OVERFLOW,
                              MULTIPLICATION_OVERFLOW,
                              SUBTRACTION_UNDERFLOW,
                              DIVISION_BY_ZERO
                          }
                          enum DowncastErrorCodes {
                              VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT32,
                              VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT64,
                              VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT96
                          }
                          // solhint-disable func-name-mixedcase
                          function Uint256BinOpError(
                              BinOpErrorCodes errorCode,
                              uint256 a,
                              uint256 b
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  UINT256_BINOP_ERROR_SELECTOR,
                                  errorCode,
                                  a,
                                  b
                              );
                          }
                          function Uint256DowncastError(
                              DowncastErrorCodes errorCode,
                              uint256 a
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  UINT256_DOWNCAST_ERROR_SELECTOR,
                                  errorCode,
                                  a
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
                      import "./IERC20TokenV06.sol";
                      library LibERC20TokenV06 {
                          bytes constant private DECIMALS_CALL_DATA = hex"313ce567";
                          /// @dev Calls `IERC20TokenV06(token).approve()`.
                          ///      Reverts if the result fails `isSuccessfulResult()` or the call reverts.
                          /// @param token The address of the token contract.
                          /// @param spender The address that receives an allowance.
                          /// @param allowance The allowance to set.
                          function compatApprove(
                              IERC20TokenV06 token,
                              address spender,
                              uint256 allowance
                          )
                              internal
                          {
                              bytes memory callData = abi.encodeWithSelector(
                                  token.approve.selector,
                                  spender,
                                  allowance
                              );
                              _callWithOptionalBooleanResult(address(token), callData);
                          }
                          /// @dev Calls `IERC20TokenV06(token).approve()` and sets the allowance to the
                          ///      maximum if the current approval is not already >= an amount.
                          ///      Reverts if the result fails `isSuccessfulResult()` or the call reverts.
                          /// @param token The address of the token contract.
                          /// @param spender The address that receives an allowance.
                          /// @param amount The minimum allowance needed.
                          function approveIfBelow(
                              IERC20TokenV06 token,
                              address spender,
                              uint256 amount
                          )
                              internal
                          {
                              if (token.allowance(address(this), spender) < amount) {
                                  compatApprove(token, spender, uint256(-1));
                              }
                          }
                          /// @dev Calls `IERC20TokenV06(token).transfer()`.
                          ///      Reverts if the result fails `isSuccessfulResult()` or the call reverts.
                          /// @param token The address of the token contract.
                          /// @param to The address that receives the tokens
                          /// @param amount Number of tokens to transfer.
                          function compatTransfer(
                              IERC20TokenV06 token,
                              address to,
                              uint256 amount
                          )
                              internal
                          {
                              bytes memory callData = abi.encodeWithSelector(
                                  token.transfer.selector,
                                  to,
                                  amount
                              );
                              _callWithOptionalBooleanResult(address(token), callData);
                          }
                          /// @dev Calls `IERC20TokenV06(token).transferFrom()`.
                          ///      Reverts if the result fails `isSuccessfulResult()` or the call reverts.
                          /// @param token The address of the token contract.
                          /// @param from The owner of the tokens.
                          /// @param to The address that receives the tokens
                          /// @param amount Number of tokens to transfer.
                          function compatTransferFrom(
                              IERC20TokenV06 token,
                              address from,
                              address to,
                              uint256 amount
                          )
                              internal
                          {
                              bytes memory callData = abi.encodeWithSelector(
                                  token.transferFrom.selector,
                                  from,
                                  to,
                                  amount
                              );
                              _callWithOptionalBooleanResult(address(token), callData);
                          }
                          /// @dev Retrieves the number of decimals for a token.
                          ///      Returns `18` if the call reverts.
                          /// @param token The address of the token contract.
                          /// @return tokenDecimals The number of decimals places for the token.
                          function compatDecimals(IERC20TokenV06 token)
                              internal
                              view
                              returns (uint8 tokenDecimals)
                          {
                              tokenDecimals = 18;
                              (bool didSucceed, bytes memory resultData) = address(token).staticcall(DECIMALS_CALL_DATA);
                              if (didSucceed && resultData.length == 32) {
                                  tokenDecimals = uint8(LibBytesV06.readUint256(resultData, 0));
                              }
                          }
                          /// @dev Retrieves the allowance for a token, owner, and spender.
                          ///      Returns `0` if the call reverts.
                          /// @param token The address of the token contract.
                          /// @param owner The owner of the tokens.
                          /// @param spender The address the spender.
                          /// @return allowance_ The allowance for a token, owner, and spender.
                          function compatAllowance(IERC20TokenV06 token, address owner, address spender)
                              internal
                              view
                              returns (uint256 allowance_)
                          {
                              (bool didSucceed, bytes memory resultData) = address(token).staticcall(
                                  abi.encodeWithSelector(
                                      token.allowance.selector,
                                      owner,
                                      spender
                                  )
                              );
                              if (didSucceed && resultData.length == 32) {
                                  allowance_ = LibBytesV06.readUint256(resultData, 0);
                              }
                          }
                          /// @dev Retrieves the balance for a token owner.
                          ///      Returns `0` if the call reverts.
                          /// @param token The address of the token contract.
                          /// @param owner The owner of the tokens.
                          /// @return balance The token balance of an owner.
                          function compatBalanceOf(IERC20TokenV06 token, address owner)
                              internal
                              view
                              returns (uint256 balance)
                          {
                              (bool didSucceed, bytes memory resultData) = address(token).staticcall(
                                  abi.encodeWithSelector(
                                      token.balanceOf.selector,
                                      owner
                                  )
                              );
                              if (didSucceed && resultData.length == 32) {
                                  balance = LibBytesV06.readUint256(resultData, 0);
                              }
                          }
                          /// @dev Check if the data returned by a non-static call to an ERC20 token
                          ///      is a successful result. Supported functions are `transfer()`,
                          ///      `transferFrom()`, and `approve()`.
                          /// @param resultData The raw data returned by a non-static call to the ERC20 token.
                          /// @return isSuccessful Whether the result data indicates success.
                          function isSuccessfulResult(bytes memory resultData)
                              internal
                              pure
                              returns (bool isSuccessful)
                          {
                              if (resultData.length == 0) {
                                  return true;
                              }
                              if (resultData.length == 32) {
                                  uint256 result = LibBytesV06.readUint256(resultData, 0);
                                  if (result == 1) {
                                      return true;
                                  }
                              }
                          }
                          /// @dev Executes a call on address `target` with calldata `callData`
                          ///      and asserts that either nothing was returned or a single boolean
                          ///      was returned equal to `true`.
                          /// @param target The call target.
                          /// @param callData The abi-encoded call data.
                          function _callWithOptionalBooleanResult(
                              address target,
                              bytes memory callData
                          )
                              private
                          {
                              (bool didSucceed, bytes memory resultData) = target.call(callData);
                              if (didSucceed && isSuccessfulResult(resultData)) {
                                  return;
                              }
                              LibRichErrorsV06.rrevert(resultData);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./LibStorage.sol";
                      import "../external/IAllowanceTarget.sol";
                      /// @dev Storage helpers for the `TokenSpender` feature.
                      library LibTokenSpenderStorage {
                          /// @dev Storage bucket for this feature.
                          struct Storage {
                              // Allowance target contract.
                              IAllowanceTarget allowanceTarget;
                          }
                          /// @dev Get the storage bucket for this contract.
                          function getStorage() internal pure returns (Storage storage stor) {
                              uint256 storageSlot = LibStorage.getStorageSlot(
                                  LibStorage.StorageId.TokenSpender
                              );
                              // Dip into assembly to change the slot pointed to by the local
                              // variable `stor`.
                              // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
                              assembly { stor_slot := storageSlot }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "../errors/LibTransformERC20RichErrors.sol";
                      import "../fixins/FixinCommon.sol";
                      import "../migrations/LibMigrate.sol";
                      import "../external/IFlashWallet.sol";
                      import "../external/FlashWallet.sol";
                      import "../storage/LibTransformERC20Storage.sol";
                      import "../transformers/IERC20Transformer.sol";
                      import "../transformers/LibERC20Transformer.sol";
                      import "./ITransformERC20.sol";
                      import "./ITokenSpender.sol";
                      import "./IFeature.sol";
                      import "./ISimpleFunctionRegistry.sol";
                      /// @dev Feature to composably transform between ERC20 tokens.
                      contract TransformERC20 is
                          IFeature,
                          ITransformERC20,
                          FixinCommon
                      {
                          /// @dev Stack vars for `_transformERC20Private()`.
                          struct TransformERC20PrivateState {
                              IFlashWallet wallet;
                              address transformerDeployer;
                              uint256 takerOutputTokenBalanceBefore;
                              uint256 takerOutputTokenBalanceAfter;
                          }
                          // solhint-disable
                          /// @dev Name of this feature.
                          string public constant override FEATURE_NAME = "TransformERC20";
                          /// @dev Version of this feature.
                          uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
                          /// @dev The implementation address of this feature.
                          address private immutable _implementation;
                          // solhint-enable
                          using LibSafeMathV06 for uint256;
                          using LibRichErrorsV06 for bytes;
                          constructor() public {
                              _implementation = address(this);
                          }
                          /// @dev Initialize and register this feature.
                          ///      Should be delegatecalled by `Migrate.migrate()`.
                          /// @param transformerDeployer The trusted deployer for transformers.
                          /// @return success `LibMigrate.SUCCESS` on success.
                          function migrate(address transformerDeployer) external returns (bytes4 success) {
                              ISimpleFunctionRegistry(address(this))
                                  .extend(this.getTransformerDeployer.selector, _implementation);
                              ISimpleFunctionRegistry(address(this))
                                  .extend(this.createTransformWallet.selector, _implementation);
                              ISimpleFunctionRegistry(address(this))
                                  .extend(this.getTransformWallet.selector, _implementation);
                              ISimpleFunctionRegistry(address(this))
                                  .extend(this.setTransformerDeployer.selector, _implementation);
                              ISimpleFunctionRegistry(address(this))
                                  .extend(this.transformERC20.selector, _implementation);
                              ISimpleFunctionRegistry(address(this))
                                  .extend(this._transformERC20.selector, _implementation);
                              createTransformWallet();
                              LibTransformERC20Storage.getStorage().transformerDeployer = transformerDeployer;
                              return LibMigrate.MIGRATE_SUCCESS;
                          }
                          /// @dev Replace the allowed deployer for transformers.
                          ///      Only callable by the owner.
                          /// @param transformerDeployer The address of the trusted deployer for transformers.
                          function setTransformerDeployer(address transformerDeployer)
                              external
                              override
                              onlyOwner
                          {
                              LibTransformERC20Storage.getStorage().transformerDeployer = transformerDeployer;
                              emit TransformerDeployerUpdated(transformerDeployer);
                          }
                          /// @dev Return the allowed deployer for transformers.
                          /// @return deployer The transform deployer address.
                          function getTransformerDeployer()
                              public
                              override
                              view
                              returns (address deployer)
                          {
                              return LibTransformERC20Storage.getStorage().transformerDeployer;
                          }
                          /// @dev Deploy a new wallet instance and replace the current one with it.
                          ///      Useful if we somehow break the current wallet instance.
                          ///      Anyone can call this.
                          /// @return wallet The new wallet instance.
                          function createTransformWallet()
                              public
                              override
                              returns (IFlashWallet wallet)
                          {
                              wallet = new FlashWallet();
                              LibTransformERC20Storage.getStorage().wallet = wallet;
                          }
                          /// @dev Executes a series of transformations to convert an ERC20 `inputToken`
                          ///      to an ERC20 `outputToken`.
                          /// @param inputToken The token being provided by the sender.
                          ///        If `0xeee...`, ETH is implied and should be provided with the call.`
                          /// @param outputToken The token to be acquired by the sender.
                          ///        `0xeee...` implies ETH.
                          /// @param inputTokenAmount The amount of `inputToken` to take from the sender.
                          ///        If set to `uint256(-1)`, the entire spendable balance of the taker
                          ///        will be solt.
                          /// @param minOutputTokenAmount The minimum amount of `outputToken` the sender
                          ///        must receive for the entire transformation to succeed. If set to zero,
                          ///        the minimum output token transfer will not be asserted.
                          /// @param transformations The transformations to execute on the token balance(s)
                          ///        in sequence.
                          /// @return outputTokenAmount The amount of `outputToken` received by the sender.
                          function transformERC20(
                              IERC20TokenV06 inputToken,
                              IERC20TokenV06 outputToken,
                              uint256 inputTokenAmount,
                              uint256 minOutputTokenAmount,
                              Transformation[] memory transformations
                          )
                              public
                              override
                              payable
                              returns (uint256 outputTokenAmount)
                          {
                              return _transformERC20Private(
                                  keccak256(msg.data),
                                  msg.sender,
                                  inputToken,
                                  outputToken,
                                  inputTokenAmount,
                                  minOutputTokenAmount,
                                  transformations
                              );
                          }
                          /// @dev Internal version of `transformERC20()`. Only callable from within.
                          /// @param callDataHash Hash of the ingress calldata.
                          /// @param taker The taker address.
                          /// @param inputToken The token being provided by the taker.
                          ///        If `0xeee...`, ETH is implied and should be provided with the call.`
                          /// @param outputToken The token to be acquired by the taker.
                          ///        `0xeee...` implies ETH.
                          /// @param inputTokenAmount The amount of `inputToken` to take from the taker.
                          ///        If set to `uint256(-1)`, the entire spendable balance of the taker
                          ///        will be solt.
                          /// @param minOutputTokenAmount The minimum amount of `outputToken` the taker
                          ///        must receive for the entire transformation to succeed. If set to zero,
                          ///        the minimum output token transfer will not be asserted.
                          /// @param transformations The transformations to execute on the token balance(s)
                          ///        in sequence.
                          /// @return outputTokenAmount The amount of `outputToken` received by the taker.
                          function _transformERC20(
                              bytes32 callDataHash,
                              address payable taker,
                              IERC20TokenV06 inputToken,
                              IERC20TokenV06 outputToken,
                              uint256 inputTokenAmount,
                              uint256 minOutputTokenAmount,
                              Transformation[] memory transformations
                          )
                              public
                              override
                              payable
                              onlySelf
                              returns (uint256 outputTokenAmount)
                          {
                              return _transformERC20Private(
                                  callDataHash,
                                  taker,
                                  inputToken,
                                  outputToken,
                                  inputTokenAmount,
                                  minOutputTokenAmount,
                                  transformations
                              );
                          }
                          /// @dev Private version of `transformERC20()`.
                          /// @param callDataHash Hash of the ingress calldata.
                          /// @param taker The taker address.
                          /// @param inputToken The token being provided by the taker.
                          ///        If `0xeee...`, ETH is implied and should be provided with the call.`
                          /// @param outputToken The token to be acquired by the taker.
                          ///        `0xeee...` implies ETH.
                          /// @param inputTokenAmount The amount of `inputToken` to take from the taker.
                          ///        If set to `uint256(-1)`, the entire spendable balance of the taker
                          ///        will be solt.
                          /// @param minOutputTokenAmount The minimum amount of `outputToken` the taker
                          ///        must receive for the entire transformation to succeed. If set to zero,
                          ///        the minimum output token transfer will not be asserted.
                          /// @param transformations The transformations to execute on the token balance(s)
                          ///        in sequence.
                          /// @return outputTokenAmount The amount of `outputToken` received by the taker.
                          function _transformERC20Private(
                              bytes32 callDataHash,
                              address payable taker,
                              IERC20TokenV06 inputToken,
                              IERC20TokenV06 outputToken,
                              uint256 inputTokenAmount,
                              uint256 minOutputTokenAmount,
                              Transformation[] memory transformations
                          )
                              private
                              returns (uint256 outputTokenAmount)
                          {
                              // If the input token amount is -1, transform the taker's entire
                              // spendable balance.
                              if (inputTokenAmount == uint256(-1)) {
                                  inputTokenAmount = ITokenSpender(address(this))
                                      .getSpendableERC20BalanceOf(inputToken, taker);
                              }
                              TransformERC20PrivateState memory state;
                              state.wallet = getTransformWallet();
                              state.transformerDeployer = getTransformerDeployer();
                              // Remember the initial output token balance of the taker.
                              state.takerOutputTokenBalanceBefore =
                                  LibERC20Transformer.getTokenBalanceOf(outputToken, taker);
                              // Pull input tokens from the taker to the wallet and transfer attached ETH.
                              _transferInputTokensAndAttachedEth(
                                  inputToken,
                                  taker,
                                  address(state.wallet),
                                  inputTokenAmount
                              );
                              // Perform transformations.
                              for (uint256 i = 0; i < transformations.length; ++i) {
                                  _executeTransformation(
                                      state.wallet,
                                      transformations[i],
                                      state.transformerDeployer,
                                      taker,
                                      callDataHash
                                  );
                              }
                              // Compute how much output token has been transferred to the taker.
                              state.takerOutputTokenBalanceAfter =
                                  LibERC20Transformer.getTokenBalanceOf(outputToken, taker);
                              if (state.takerOutputTokenBalanceAfter > state.takerOutputTokenBalanceBefore) {
                                  outputTokenAmount = state.takerOutputTokenBalanceAfter.safeSub(
                                      state.takerOutputTokenBalanceBefore
                                  );
                              } else if (state.takerOutputTokenBalanceAfter < state.takerOutputTokenBalanceBefore) {
                                  LibTransformERC20RichErrors.NegativeTransformERC20OutputError(
                                      address(outputToken),
                                      state.takerOutputTokenBalanceBefore - state.takerOutputTokenBalanceAfter
                                  ).rrevert();
                              }
                              // Ensure enough output token has been sent to the taker.
                              if (outputTokenAmount < minOutputTokenAmount) {
                                  LibTransformERC20RichErrors.IncompleteTransformERC20Error(
                                      address(outputToken),
                                      outputTokenAmount,
                                      minOutputTokenAmount
                                  ).rrevert();
                              }
                              // Emit an event.
                              emit TransformedERC20(
                                  taker,
                                  address(inputToken),
                                  address(outputToken),
                                  inputTokenAmount,
                                  outputTokenAmount
                              );
                          }
                          /// @dev Return the current wallet instance that will serve as the execution
                          ///      context for transformations.
                          /// @return wallet The wallet instance.
                          function getTransformWallet()
                              public
                              override
                              view
                              returns (IFlashWallet wallet)
                          {
                              return LibTransformERC20Storage.getStorage().wallet;
                          }
                          /// @dev Transfer input tokens from the taker and any attached ETH to `to`
                          /// @param inputToken The token to pull from the taker.
                          /// @param from The from (taker) address.
                          /// @param to The recipient of tokens and ETH.
                          /// @param amount Amount of `inputToken` tokens to transfer.
                          function _transferInputTokensAndAttachedEth(
                              IERC20TokenV06 inputToken,
                              address from,
                              address payable to,
                              uint256 amount
                          )
                              private
                          {
                              // Transfer any attached ETH.
                              if (msg.value != 0) {
                                  to.transfer(msg.value);
                              }
                              // Transfer input tokens.
                              if (!LibERC20Transformer.isTokenETH(inputToken)) {
                                  // Token is not ETH, so pull ERC20 tokens.
                                  ITokenSpender(address(this))._spendERC20Tokens(
                                      inputToken,
                                      from,
                                      to,
                                      amount
                                  );
                              } else if (msg.value < amount) {
                                   // Token is ETH, so the caller must attach enough ETH to the call.
                                  LibTransformERC20RichErrors.InsufficientEthAttachedError(
                                      msg.value,
                                      amount
                                  ).rrevert();
                              }
                          }
                          /// @dev Executs a transformer in the context of `wallet`.
                          /// @param wallet The wallet instance.
                          /// @param transformation The transformation.
                          /// @param transformerDeployer The address of the transformer deployer.
                          /// @param taker The taker address.
                          /// @param callDataHash Hash of the calldata.
                          function _executeTransformation(
                              IFlashWallet wallet,
                              Transformation memory transformation,
                              address transformerDeployer,
                              address payable taker,
                              bytes32 callDataHash
                          )
                              private
                          {
                              // Derive the transformer address from the deployment nonce.
                              address payable transformer = LibERC20Transformer.getDeployedAddress(
                                  transformerDeployer,
                                  transformation.deploymentNonce
                              );
                              // Call `transformer.transform()` as the wallet.
                              bytes memory resultData = wallet.executeDelegateCall(
                                  // The call target.
                                  transformer,
                                  // Call data.
                                  abi.encodeWithSelector(
                                      IERC20Transformer.transform.selector,
                                      callDataHash,
                                      taker,
                                      transformation.data
                                  )
                              );
                              // Ensure the transformer returned the magic bytes.
                              if (resultData.length != 32 ||
                                  abi.decode(resultData, (bytes4)) != LibERC20Transformer.TRANSFORMER_SUCCESS
                              ) {
                                  LibTransformERC20RichErrors.TransformerFailedError(
                                      transformer,
                                      transformation.data,
                                      resultData
                                  ).rrevert();
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./LibStorage.sol";
                      import "../external/IFlashWallet.sol";
                      /// @dev Storage helpers for the `TokenSpender` feature.
                      library LibTransformERC20Storage {
                          /// @dev Storage bucket for this feature.
                          struct Storage {
                              // The current wallet instance.
                              IFlashWallet wallet;
                              // The transformer deployer address.
                              address transformerDeployer;
                          }
                          /// @dev Get the storage bucket for this contract.
                          function getStorage() internal pure returns (Storage storage stor) {
                              uint256 storageSlot = LibStorage.getStorageSlot(
                                  LibStorage.StorageId.TransformERC20
                              );
                              // Dip into assembly to change the slot pointed to by the local
                              // variable `stor`.
                              // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
                              assembly { stor_slot := storageSlot }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
                      library LibERC20Transformer {
                          using LibERC20TokenV06 for IERC20TokenV06;
                          /// @dev ETH pseudo-token address.
                          address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                          /// @dev Return value indicating success in `IERC20Transformer.transform()`.
                          ///      This is just `keccak256('TRANSFORMER_SUCCESS')`.
                          bytes4 constant internal TRANSFORMER_SUCCESS = 0x13c9929e;
                          /// @dev Transfer ERC20 tokens and ETH.
                          /// @param token An ERC20 or the ETH pseudo-token address (`ETH_TOKEN_ADDRESS`).
                          /// @param to The recipient.
                          /// @param amount The transfer amount.
                          function transformerTransfer(
                              IERC20TokenV06 token,
                              address payable to,
                              uint256 amount
                          )
                              internal
                          {
                              if (isTokenETH(token)) {
                                  to.transfer(amount);
                              } else {
                                  token.compatTransfer(to, amount);
                              }
                          }
                          /// @dev Check if a token is the ETH pseudo-token.
                          /// @param token The token to check.
                          /// @return isETH `true` if the token is the ETH pseudo-token.
                          function isTokenETH(IERC20TokenV06 token)
                              internal
                              pure
                              returns (bool isETH)
                          {
                              return address(token) == ETH_TOKEN_ADDRESS;
                          }
                          /// @dev Check the balance of an ERC20 token or ETH.
                          /// @param token An ERC20 or the ETH pseudo-token address (`ETH_TOKEN_ADDRESS`).
                          /// @param owner Holder of the tokens.
                          /// @return tokenBalance The balance of `owner`.
                          function getTokenBalanceOf(IERC20TokenV06 token, address owner)
                              internal
                              view
                              returns (uint256 tokenBalance)
                          {
                              if (isTokenETH(token)) {
                                  return owner.balance;
                              }
                              return token.balanceOf(owner);
                          }
                          /// @dev RLP-encode a 32-bit or less account nonce.
                          /// @param nonce A positive integer in the range 0 <= nonce < 2^32.
                          /// @return rlpNonce The RLP encoding.
                          function rlpEncodeNonce(uint32 nonce)
                              internal
                              pure
                              returns (bytes memory rlpNonce)
                          {
                              // See https://github.com/ethereum/wiki/wiki/RLP for RLP encoding rules.
                              if (nonce == 0) {
                                  rlpNonce = new bytes(1);
                                  rlpNonce[0] = 0x80;
                              } else if (nonce < 0x80) {
                                  rlpNonce = new bytes(1);
                                  rlpNonce[0] = byte(uint8(nonce));
                              } else if (nonce <= 0xFF) {
                                  rlpNonce = new bytes(2);
                                  rlpNonce[0] = 0x81;
                                  rlpNonce[1] = byte(uint8(nonce));
                              } else if (nonce <= 0xFFFF) {
                                  rlpNonce = new bytes(3);
                                  rlpNonce[0] = 0x82;
                                  rlpNonce[1] = byte(uint8((nonce & 0xFF00) >> 8));
                                  rlpNonce[2] = byte(uint8(nonce));
                              } else if (nonce <= 0xFFFFFF) {
                                  rlpNonce = new bytes(4);
                                  rlpNonce[0] = 0x83;
                                  rlpNonce[1] = byte(uint8((nonce & 0xFF0000) >> 16));
                                  rlpNonce[2] = byte(uint8((nonce & 0xFF00) >> 8));
                                  rlpNonce[3] = byte(uint8(nonce));
                              } else {
                                  rlpNonce = new bytes(5);
                                  rlpNonce[0] = 0x84;
                                  rlpNonce[1] = byte(uint8((nonce & 0xFF000000) >> 24));
                                  rlpNonce[2] = byte(uint8((nonce & 0xFF0000) >> 16));
                                  rlpNonce[3] = byte(uint8((nonce & 0xFF00) >> 8));
                                  rlpNonce[4] = byte(uint8(nonce));
                              }
                          }
                          /// @dev Compute the expected deployment address by `deployer` at
                          ///      the nonce given by `deploymentNonce`.
                          /// @param deployer The address of the deployer.
                          /// @param deploymentNonce The nonce that the deployer had when deploying
                          ///        a contract.
                          /// @return deploymentAddress The deployment address.
                          function getDeployedAddress(address deployer, uint32 deploymentNonce)
                              internal
                              pure
                              returns (address payable deploymentAddress)
                          {
                              // The address of if a deployed contract is the lower 20 bytes of the
                              // hash of the RLP-encoded deployer's account address + account nonce.
                              // See: https://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
                              bytes memory rlpNonce = rlpEncodeNonce(deploymentNonce);
                              return address(uint160(uint256(keccak256(abi.encodePacked(
                                  byte(uint8(0xC0 + 21 + rlpNonce.length)),
                                  byte(uint8(0x80 + 20)),
                                  deployer,
                                  rlpNonce
                              )))));
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../ZeroEx.sol";
                      import "../features/IOwnable.sol";
                      import "../features/TokenSpender.sol";
                      import "../features/TransformERC20.sol";
                      import "../external/AllowanceTarget.sol";
                      import "./InitialMigration.sol";
                      /// @dev A contract for deploying and configuring the full ZeroEx contract.
                      contract FullMigration {
                          // solhint-disable no-empty-blocks,indent
                          /// @dev Features to add the the proxy contract.
                          struct Features {
                              SimpleFunctionRegistry registry;
                              Ownable ownable;
                              TokenSpender tokenSpender;
                              TransformERC20 transformERC20;
                          }
                          /// @dev Parameters needed to initialize features.
                          struct MigrateOpts {
                              address transformerDeployer;
                          }
                          /// @dev The allowed caller of `deploy()`.
                          address public immutable deployer;
                          /// @dev The initial migration contract.
                          InitialMigration private _initialMigration;
                          /// @dev Instantiate this contract and set the allowed caller of `deploy()`
                          ///      to `deployer`.
                          /// @param deployer_ The allowed caller of `deploy()`.
                          constructor(address payable deployer_)
                              public
                          {
                              deployer = deployer_;
                              // Create an initial migration contract with this contract set to the
                              // allowed deployer.
                              _initialMigration = new InitialMigration(address(this));
                          }
                          /// @dev Deploy the `ZeroEx` contract with the full feature set,
                          ///      transfer ownership to `owner`, then self-destruct.
                          /// @param owner The owner of the contract.
                          /// @param features Features to add to the proxy.
                          /// @return zeroEx The deployed and configured `ZeroEx` contract.
                          /// @param migrateOpts Parameters needed to initialize features.
                          function deploy(
                              address payable owner,
                              Features memory features,
                              MigrateOpts memory migrateOpts
                          )
                              public
                              returns (ZeroEx zeroEx)
                          {
                              require(msg.sender == deployer, "FullMigration/INVALID_SENDER");
                              // Perform the initial migration with the owner set to this contract.
                              zeroEx = _initialMigration.deploy(
                                  address(uint160(address(this))),
                                  InitialMigration.BootstrapFeatures({
                                      registry: features.registry,
                                      ownable: features.ownable
                                  })
                              );
                              // Add features.
                              _addFeatures(zeroEx, owner, features, migrateOpts);
                              // Transfer ownership to the real owner.
                              IOwnable(address(zeroEx)).transferOwnership(owner);
                              // Self-destruct.
                              this.die(owner);
                          }
                          /// @dev Destroy this contract. Only callable from ourselves (from `deploy()`).
                          /// @param ethRecipient Receiver of any ETH in this contract.
                          function die(address payable ethRecipient)
                              external
                              virtual
                          {
                              require(msg.sender == address(this), "FullMigration/INVALID_SENDER");
                              // This contract should not hold any funds but we send
                              // them to the ethRecipient just in case.
                              selfdestruct(ethRecipient);
                          }
                          /// @dev Deploy and register features to the ZeroEx contract.
                          /// @param zeroEx The bootstrapped ZeroEx contract.
                          /// @param owner The ultimate owner of the ZeroEx contract.
                          /// @param features Features to add to the proxy.
                          /// @param migrateOpts Parameters needed to initialize features.
                          function _addFeatures(
                              ZeroEx zeroEx,
                              address owner,
                              Features memory features,
                              MigrateOpts memory migrateOpts
                          )
                              private
                          {
                              IOwnable ownable = IOwnable(address(zeroEx));
                              // TokenSpender
                              {
                                  // Create the allowance target.
                                  AllowanceTarget allowanceTarget = new AllowanceTarget();
                                  // Let the ZeroEx contract use the allowance target.
                                  allowanceTarget.addAuthorizedAddress(address(zeroEx));
                                  // Transfer ownership of the allowance target to the (real) owner.
                                  allowanceTarget.transferOwnership(owner);
                                  // Register the feature.
                                  ownable.migrate(
                                      address(features.tokenSpender),
                                      abi.encodeWithSelector(
                                          TokenSpender.migrate.selector,
                                          allowanceTarget
                                      ),
                                      address(this)
                                  );
                              }
                              // TransformERC20
                              {
                                  // Register the feature.
                                  ownable.migrate(
                                      address(features.transformERC20),
                                      abi.encodeWithSelector(
                                          TransformERC20.migrate.selector,
                                          migrateOpts.transformerDeployer
                                      ),
                                      address(this)
                                  );
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../ZeroEx.sol";
                      import "../features/IBootstrap.sol";
                      import "../features/SimpleFunctionRegistry.sol";
                      import "../features/Ownable.sol";
                      import "./LibBootstrap.sol";
                      /// @dev A contract for deploying and configuring a minimal ZeroEx contract.
                      contract InitialMigration {
                          /// @dev Features to bootstrap into the the proxy contract.
                          struct BootstrapFeatures {
                              SimpleFunctionRegistry registry;
                              Ownable ownable;
                          }
                          /// @dev The allowed caller of `deploy()`. In production, this would be
                          ///      the governor.
                          address public immutable deployer;
                          /// @dev The real address of this contract.
                          address private immutable _implementation;
                          /// @dev Instantiate this contract and set the allowed caller of `deploy()`
                          ///      to `deployer_`.
                          /// @param deployer_ The allowed caller of `deploy()`.
                          constructor(address deployer_) public {
                              deployer = deployer_;
                              _implementation = address(this);
                          }
                          /// @dev Deploy the `ZeroEx` contract with the minimum feature set,
                          ///      transfers ownership to `owner`, then self-destructs.
                          ///      Only callable by `deployer` set in the contstructor.
                          /// @param owner The owner of the contract.
                          /// @param features Features to bootstrap into the proxy.
                          /// @return zeroEx The deployed and configured `ZeroEx` contract.
                          function deploy(address payable owner, BootstrapFeatures memory features)
                              public
                              virtual
                              returns (ZeroEx zeroEx)
                          {
                              // Must be called by the allowed deployer.
                              require(msg.sender == deployer, "InitialMigration/INVALID_SENDER");
                              // Deploy the ZeroEx contract, setting ourselves as the bootstrapper.
                              zeroEx = new ZeroEx();
                              // Bootstrap the initial feature set.
                              IBootstrap(address(zeroEx)).bootstrap(
                                  address(this),
                                  abi.encodeWithSelector(this.bootstrap.selector, owner, features)
                              );
                              // Self-destruct. This contract should not hold any funds but we send
                              // them to the owner just in case.
                              this.die(owner);
                          }
                          /// @dev Sets up the initial state of the `ZeroEx` contract.
                          ///      The `ZeroEx` contract will delegatecall into this function.
                          /// @param owner The new owner of the ZeroEx contract.
                          /// @param features Features to bootstrap into the proxy.
                          /// @return success Magic bytes if successful.
                          function bootstrap(address owner, BootstrapFeatures memory features)
                              public
                              virtual
                              returns (bytes4 success)
                          {
                              // Deploy and migrate the initial features.
                              // Order matters here.
                              // Initialize Registry.
                              LibBootstrap.delegatecallBootstrapFunction(
                                  address(features.registry),
                                  abi.encodeWithSelector(
                                      SimpleFunctionRegistry.bootstrap.selector
                                  )
                              );
                              // Initialize Ownable.
                              LibBootstrap.delegatecallBootstrapFunction(
                                  address(features.ownable),
                                  abi.encodeWithSelector(
                                      Ownable.bootstrap.selector
                                  )
                              );
                              // De-register `SimpleFunctionRegistry._extendSelf`.
                              SimpleFunctionRegistry(address(this)).rollback(
                                  SimpleFunctionRegistry._extendSelf.selector,
                                  address(0)
                              );
                              // Transfer ownership to the real owner.
                              Ownable(address(this)).transferOwnership(owner);
                              success = LibBootstrap.BOOTSTRAP_SUCCESS;
                          }
                          /// @dev Self-destructs this contract. Only callable by this contract.
                          /// @param ethRecipient Who to transfer outstanding ETH to.
                          function die(address payable ethRecipient) public virtual {
                              require(msg.sender == _implementation, "InitialMigration/INVALID_SENDER");
                              selfdestruct(ethRecipient);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
                      import "../errors/LibTransformERC20RichErrors.sol";
                      import "./Transformer.sol";
                      import "./LibERC20Transformer.sol";
                      /// @dev A transformer that transfers tokens to arbitrary addresses.
                      contract AffiliateFeeTransformer is
                          Transformer
                      {
                          // solhint-disable no-empty-blocks
                          using LibRichErrorsV06 for bytes;
                          using LibSafeMathV06 for uint256;
                          using LibERC20Transformer for IERC20TokenV06;
                          /// @dev Information for a single fee.
                          struct TokenFee {
                              // The token to transfer to `recipient`.
                              IERC20TokenV06 token;
                              // Amount of each `token` to transfer to `recipient`.
                              // If `amount == uint256(-1)`, the entire balance of `token` will be
                              // transferred.
                              uint256 amount;
                              // Recipient of `token`.
                              address payable recipient;
                          }
                          uint256 private constant MAX_UINT256 = uint256(-1);
                          /// @dev Create this contract.
                          constructor()
                              public
                              Transformer()
                          {}
                          /// @dev Transfers tokens to recipients.
                          /// @param data ABI-encoded `TokenFee[]`, indicating which tokens to transfer.
                          /// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
                          function transform(
                              bytes32, // callDataHash,
                              address payable, // taker,
                              bytes calldata data
                          )
                              external
                              override
                              returns (bytes4 success)
                          {
                              TokenFee[] memory fees = abi.decode(data, (TokenFee[]));
                              // Transfer tokens to recipients.
                              for (uint256 i = 0; i < fees.length; ++i) {
                                  uint256 amount = fees[i].amount;
                                  if (amount == MAX_UINT256) {
                                      amount = LibERC20Transformer.getTokenBalanceOf(fees[i].token, address(this));
                                  }
                                  if (amount != 0) {
                                      fees[i].token.transformerTransfer(fees[i].recipient, amount);
                                  }
                              }
                              return LibERC20Transformer.TRANSFORMER_SUCCESS;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "../errors/LibTransformERC20RichErrors.sol";
                      import "./IERC20Transformer.sol";
                      /// @dev Abstract base class for transformers.
                      abstract contract Transformer is
                          IERC20Transformer
                      {
                          using LibRichErrorsV06 for bytes;
                          /// @dev The address of the deployer.
                          address public immutable deployer;
                          /// @dev The original address of this contract.
                          address private immutable _implementation;
                          /// @dev Create this contract.
                          constructor() public {
                              deployer = msg.sender;
                              _implementation = address(this);
                          }
                          /// @dev Destruct this contract. Only callable by the deployer and will not
                          ///      succeed in the context of a delegatecall (from another contract).
                          /// @param ethRecipient The recipient of ETH held in this contract.
                          function die(address payable ethRecipient)
                              external
                              virtual
                          {
                              // Only the deployer can call this.
                              if (msg.sender != deployer) {
                                  LibTransformERC20RichErrors
                                      .OnlyCallableByDeployerError(msg.sender, deployer)
                                      .rrevert();
                              }
                              // Must be executing our own context.
                              if (address(this) != _implementation) {
                                  LibTransformERC20RichErrors
                                      .InvalidExecutionContextError(address(this), _implementation)
                                      .rrevert();
                              }
                              selfdestruct(ethRecipient);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
                      import "../errors/LibTransformERC20RichErrors.sol";
                      import "../vendor/v3/IExchange.sol";
                      import "./Transformer.sol";
                      import "./LibERC20Transformer.sol";
                      /// @dev A transformer that fills an ERC20 market sell/buy quote.
                      contract FillQuoteTransformer is
                          Transformer
                      {
                          using LibERC20TokenV06 for IERC20TokenV06;
                          using LibERC20Transformer for IERC20TokenV06;
                          using LibSafeMathV06 for uint256;
                          using LibRichErrorsV06 for bytes;
                          /// @dev Whether we are performing a market sell or buy.
                          enum Side {
                              Sell,
                              Buy
                          }
                          /// @dev Transform data to ABI-encode and pass into `transform()`.
                          struct TransformData {
                              // Whether we aer performing a market sell or buy.
                              Side side;
                              // The token being sold.
                              // This should be an actual token, not the ETH pseudo-token.
                              IERC20TokenV06 sellToken;
                              // The token being bought.
                              // This should be an actual token, not the ETH pseudo-token.
                              IERC20TokenV06 buyToken;
                              // The orders to fill.
                              IExchange.Order[] orders;
                              // Signatures for each respective order in `orders`.
                              bytes[] signatures;
                              // Maximum fill amount for each order. This may be shorter than the
                              // number of orders, where missing entries will be treated as `uint256(-1)`.
                              // For sells, this will be the maximum sell amount (taker asset).
                              // For buys, this will be the maximum buy amount (maker asset).
                              uint256[] maxOrderFillAmounts;
                              // Amount of `sellToken` to sell or `buyToken` to buy.
                              // For sells, this may be `uint256(-1)` to sell the entire balance of
                              // `sellToken`.
                              uint256 fillAmount;
                          }
                          /// @dev Results of a call to `_fillOrder()`.
                          struct FillOrderResults {
                              // The amount of taker tokens sold, according to balance checks.
                              uint256 takerTokenSoldAmount;
                              // The amount of maker tokens sold, according to balance checks.
                              uint256 makerTokenBoughtAmount;
                              // The amount of protocol fee paid.
                              uint256 protocolFeePaid;
                          }
                          /// @dev The Exchange ERC20Proxy ID.
                          bytes4 private constant ERC20_ASSET_PROXY_ID = 0xf47261b0;
                          /// @dev Maximum uint256 value.
                          uint256 private constant MAX_UINT256 = uint256(-1);
                          /// @dev The Exchange contract.
                          IExchange public immutable exchange;
                          /// @dev The ERC20Proxy address.
                          address public immutable erc20Proxy;
                          /// @dev Create this contract.
                          /// @param exchange_ The Exchange V3 instance.
                          constructor(IExchange exchange_)
                              public
                              Transformer()
                          {
                              exchange = exchange_;
                              erc20Proxy = exchange_.getAssetProxy(ERC20_ASSET_PROXY_ID);
                          }
                          /// @dev Sell this contract's entire balance of of `sellToken` in exchange
                          ///      for `buyToken` by filling `orders`. Protocol fees should be attached
                          ///      to this call. `buyToken` and excess ETH will be transferred back to the caller.
                          /// @param data_ ABI-encoded `TransformData`.
                          /// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
                          function transform(
                              bytes32, // callDataHash,
                              address payable, // taker,
                              bytes calldata data_
                          )
                              external
                              override
                              returns (bytes4 success)
                          {
                              TransformData memory data = abi.decode(data_, (TransformData));
                              // Validate data fields.
                              if (data.sellToken.isTokenETH() || data.buyToken.isTokenETH()) {
                                  LibTransformERC20RichErrors.InvalidTransformDataError(
                                      LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_TOKENS,
                                      data_
                                  ).rrevert();
                              }
                              if (data.orders.length != data.signatures.length) {
                                  LibTransformERC20RichErrors.InvalidTransformDataError(
                                      LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_ARRAY_LENGTH,
                                      data_
                                  ).rrevert();
                              }
                              if (data.side == Side.Sell && data.fillAmount == MAX_UINT256) {
                                  // If `sellAmount == -1 then we are selling
                                  // the entire balance of `sellToken`. This is useful in cases where
                                  // the exact sell amount is not exactly known in advance, like when
                                  // unwrapping Chai/cUSDC/cDAI.
                                  data.fillAmount = data.sellToken.getTokenBalanceOf(address(this));
                              }
                              // Approve the ERC20 proxy to spend `sellToken`.
                              data.sellToken.approveIfBelow(erc20Proxy, data.fillAmount);
                              // Fill the orders.
                              uint256 singleProtocolFee = exchange.protocolFeeMultiplier().safeMul(tx.gasprice);
                              uint256 ethRemaining = address(this).balance;
                              uint256 boughtAmount = 0;
                              uint256 soldAmount = 0;
                              for (uint256 i = 0; i < data.orders.length; ++i) {
                                  // Check if we've hit our targets.
                                  if (data.side == Side.Sell) {
                                      // Market sell check.
                                      if (soldAmount >= data.fillAmount) {
                                          break;
                                      }
                                  } else {
                                      // Market buy check.
                                      if (boughtAmount >= data.fillAmount) {
                                          break;
                                      }
                                  }
                                  // Ensure we have enough ETH to cover the protocol fee.
                                  if (ethRemaining < singleProtocolFee) {
                                      LibTransformERC20RichErrors
                                          .InsufficientProtocolFeeError(ethRemaining, singleProtocolFee)
                                          .rrevert();
                                  }
                                  // Fill the order.
                                  FillOrderResults memory results;
                                  if (data.side == Side.Sell) {
                                      // Market sell.
                                      results = _sellToOrder(
                                          data.buyToken,
                                          data.sellToken,
                                          data.orders[i],
                                          data.signatures[i],
                                          data.fillAmount.safeSub(soldAmount).min256(
                                              data.maxOrderFillAmounts.length > i
                                              ? data.maxOrderFillAmounts[i]
                                              : MAX_UINT256
                                          ),
                                          singleProtocolFee
                                      );
                                  } else {
                                      // Market buy.
                                      results = _buyFromOrder(
                                          data.buyToken,
                                          data.sellToken,
                                          data.orders[i],
                                          data.signatures[i],
                                          data.fillAmount.safeSub(boughtAmount).min256(
                                              data.maxOrderFillAmounts.length > i
                                              ? data.maxOrderFillAmounts[i]
                                              : MAX_UINT256
                                          ),
                                          singleProtocolFee
                                      );
                                  }
                                  // Accumulate totals.
                                  soldAmount = soldAmount.safeAdd(results.takerTokenSoldAmount);
                                  boughtAmount = boughtAmount.safeAdd(results.makerTokenBoughtAmount);
                                  ethRemaining = ethRemaining.safeSub(results.protocolFeePaid);
                              }
                              // Ensure we hit our targets.
                              if (data.side == Side.Sell) {
                                  // Market sell check.
                                  if (soldAmount < data.fillAmount) {
                                      LibTransformERC20RichErrors
                                          .IncompleteFillSellQuoteError(
                                              address(data.sellToken),
                                              soldAmount,
                                              data.fillAmount
                                          ).rrevert();
                                  }
                              } else {
                                  // Market buy check.
                                  if (boughtAmount < data.fillAmount) {
                                      LibTransformERC20RichErrors
                                          .IncompleteFillBuyQuoteError(
                                              address(data.buyToken),
                                              boughtAmount,
                                              data.fillAmount
                                          ).rrevert();
                                  }
                              }
                              return LibERC20Transformer.TRANSFORMER_SUCCESS;
                          }
                          /// @dev Try to sell up to `sellAmount` from an order.
                          /// @param makerToken The maker/buy token.
                          /// @param takerToken The taker/sell token.
                          /// @param order The order to fill.
                          /// @param signature The signature for `order`.
                          /// @param sellAmount Amount of taker token to sell.
                          /// @param protocolFee The protocol fee needed to fill `order`.
                          function _sellToOrder(
                              IERC20TokenV06 makerToken,
                              IERC20TokenV06 takerToken,
                              IExchange.Order memory order,
                              bytes memory signature,
                              uint256 sellAmount,
                              uint256 protocolFee
                          )
                              private
                              returns (FillOrderResults memory results)
                          {
                              IERC20TokenV06 takerFeeToken =
                                  _getTokenFromERC20AssetData(order.takerFeeAssetData);
                              uint256 takerTokenFillAmount = sellAmount;
                              if (order.takerFee != 0) {
                                  if (takerFeeToken == makerToken) {
                                      // Taker fee is payable in the maker token, so we need to
                                      // approve the proxy to spend the maker token.
                                      // It isn't worth computing the actual taker fee
                                      // since `approveIfBelow()` will set the allowance to infinite. We
                                      // just need a reasonable upper bound to avoid unnecessarily re-approving.
                                      takerFeeToken.approveIfBelow(erc20Proxy, order.takerFee);
                                  } else if (takerFeeToken == takerToken){
                                      // Taker fee is payable in the taker token, so we need to
                                      // reduce the fill amount to cover the fee.
                                      // takerTokenFillAmount' =
                                      //   (takerTokenFillAmount * order.takerAssetAmount) /
                                      //   (order.takerAssetAmount + order.takerFee)
                                      takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
                                          order.takerAssetAmount,
                                          order.takerAssetAmount.safeAdd(order.takerFee),
                                          sellAmount
                                      );
                                  } else {
                                      //  Only support taker or maker asset denominated taker fees.
                                      LibTransformERC20RichErrors.InvalidTakerFeeTokenError(
                                          address(takerFeeToken)
                                      ).rrevert();
                                  }
                              }
                              // Clamp fill amount to order size.
                              takerTokenFillAmount = LibSafeMathV06.min256(
                                  takerTokenFillAmount,
                                  order.takerAssetAmount
                              );
                              // Perform the fill.
                              return _fillOrder(
                                  order,
                                  signature,
                                  takerTokenFillAmount,
                                  protocolFee,
                                  makerToken,
                                  takerFeeToken == takerToken
                              );
                          }
                          /// @dev Try to buy up to `buyAmount` from an order.
                          /// @param makerToken The maker/buy token.
                          /// @param takerToken The taker/sell token.
                          /// @param order The order to fill.
                          /// @param signature The signature for `order`.
                          /// @param buyAmount Amount of maker token to buy.
                          /// @param protocolFee The protocol fee needed to fill `order`.
                          function _buyFromOrder(
                              IERC20TokenV06 makerToken,
                              IERC20TokenV06 takerToken,
                              IExchange.Order memory order,
                              bytes memory signature,
                              uint256 buyAmount,
                              uint256 protocolFee
                          )
                              private
                              returns (FillOrderResults memory results)
                          {
                              IERC20TokenV06 takerFeeToken =
                                  _getTokenFromERC20AssetData(order.takerFeeAssetData);
                              // Compute the default taker token fill amount.
                              uint256 takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
                                  buyAmount,
                                  order.makerAssetAmount,
                                  order.takerAssetAmount
                              );
                              if (order.takerFee != 0) {
                                  if (takerFeeToken == makerToken) {
                                      // Taker fee is payable in the maker token.
                                      // Adjust the taker token fill amount to account for maker
                                      // tokens being lost to the taker fee.
                                      // takerTokenFillAmount' =
                                      //  (order.takerAssetAmount * buyAmount) /
                                      //  (order.makerAssetAmount - order.takerFee)
                                      takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
                                          buyAmount,
                                          order.makerAssetAmount.safeSub(order.takerFee),
                                          order.takerAssetAmount
                                      );
                                      // Approve the proxy to spend the maker token.
                                      // It isn't worth computing the actual taker fee
                                      // since `approveIfBelow()` will set the allowance to infinite. We
                                      // just need a reasonable upper bound to avoid unnecessarily re-approving.
                                      takerFeeToken.approveIfBelow(erc20Proxy, order.takerFee);
                                  } else if (takerFeeToken != takerToken) {
                                      //  Only support taker or maker asset denominated taker fees.
                                      LibTransformERC20RichErrors.InvalidTakerFeeTokenError(
                                          address(takerFeeToken)
                                      ).rrevert();
                                  }
                              }
                              // Clamp to order size.
                              takerTokenFillAmount = LibSafeMathV06.min256(
                                  order.takerAssetAmount,
                                  takerTokenFillAmount
                              );
                              // Perform the fill.
                              return _fillOrder(
                                  order,
                                  signature,
                                  takerTokenFillAmount,
                                  protocolFee,
                                  makerToken,
                                  takerFeeToken == takerToken
                              );
                          }
                          /// @dev Attempt to fill an order. If the fill reverts, the revert will be
                          ///      swallowed and `results` will be zeroed out.
                          /// @param order The order to fill.
                          /// @param signature The order signature.
                          /// @param takerAssetFillAmount How much taker asset to fill.
                          /// @param protocolFee The protocol fee needed to fill this order.
                          /// @param makerToken The maker token.
                          /// @param isTakerFeeInTakerToken Whether the taker fee token is the same as the
                          ///        taker token.
                          function _fillOrder(
                              IExchange.Order memory order,
                              bytes memory signature,
                              uint256 takerAssetFillAmount,
                              uint256 protocolFee,
                              IERC20TokenV06 makerToken,
                              bool isTakerFeeInTakerToken
                          )
                              private
                              returns (FillOrderResults memory results)
                          {
                              // Track changes in the maker token balance.
                              uint256 initialMakerTokenBalance = makerToken.balanceOf(address(this));
                              try
                                  exchange.fillOrder
                                      {value: protocolFee}
                                      (order, takerAssetFillAmount, signature)
                                  returns (IExchange.FillResults memory fillResults)
                              {
                                  // Update maker quantity based on changes in token balances.
                                  results.makerTokenBoughtAmount = makerToken.balanceOf(address(this))
                                      .safeSub(initialMakerTokenBalance);
                                  // We can trust the other fill result quantities.
                                  results.protocolFeePaid = fillResults.protocolFeePaid;
                                  results.takerTokenSoldAmount = fillResults.takerAssetFilledAmount;
                                  // If the taker fee is payable in the taker asset, include the
                                  // taker fee in the total amount sold.
                                  if (isTakerFeeInTakerToken) {
                                      results.takerTokenSoldAmount =
                                          results.takerTokenSoldAmount.safeAdd(fillResults.takerFeePaid);
                                  }
                              } catch (bytes memory) {
                                  // Swallow failures, leaving all results as zero.
                              }
                          }
                          /// @dev Extract the token from plain ERC20 asset data.
                          ///      If the asset-data is empty, a zero token address will be returned.
                          /// @param assetData The order asset data.
                          function _getTokenFromERC20AssetData(bytes memory assetData)
                              private
                              pure
                              returns (IERC20TokenV06 token)
                          {
                              if (assetData.length == 0) {
                                  return IERC20TokenV06(address(0));
                              }
                              if (assetData.length != 36 ||
                                  LibBytesV06.readBytes4(assetData, 0) != ERC20_ASSET_PROXY_ID)
                              {
                                  LibTransformERC20RichErrors
                                      .InvalidERC20AssetDataError(assetData)
                                      .rrevert();
                              }
                              return IERC20TokenV06(LibBytesV06.readAddress(assetData, 16));
                          }
                      }
                      /*
                        Copyright 2019 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./LibSafeMathV06.sol";
                      import "./errors/LibRichErrorsV06.sol";
                      import "./errors/LibMathRichErrorsV06.sol";
                      library LibMathV06 {
                          using LibSafeMathV06 for uint256;
                          /// @dev Calculates partial value given a numerator and denominator rounded down.
                          ///      Reverts if rounding error is >= 0.1%
                          /// @param numerator Numerator.
                          /// @param denominator Denominator.
                          /// @param target Value to calculate partial of.
                          /// @return partialAmount Partial value of target rounded down.
                          function safeGetPartialAmountFloor(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (uint256 partialAmount)
                          {
                              if (isRoundingErrorFloor(
                                      numerator,
                                      denominator,
                                      target
                              )) {
                                  LibRichErrorsV06.rrevert(LibMathRichErrorsV06.RoundingError(
                                      numerator,
                                      denominator,
                                      target
                                  ));
                              }
                              partialAmount = numerator.safeMul(target).safeDiv(denominator);
                              return partialAmount;
                          }
                          /// @dev Calculates partial value given a numerator and denominator rounded down.
                          ///      Reverts if rounding error is >= 0.1%
                          /// @param numerator Numerator.
                          /// @param denominator Denominator.
                          /// @param target Value to calculate partial of.
                          /// @return partialAmount Partial value of target rounded up.
                          function safeGetPartialAmountCeil(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (uint256 partialAmount)
                          {
                              if (isRoundingErrorCeil(
                                      numerator,
                                      denominator,
                                      target
                              )) {
                                  LibRichErrorsV06.rrevert(LibMathRichErrorsV06.RoundingError(
                                      numerator,
                                      denominator,
                                      target
                                  ));
                              }
                              // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
                              //       ceil(a / b) = floor((a + b - 1) / b)
                              // To implement `ceil(a / b)` using safeDiv.
                              partialAmount = numerator.safeMul(target)
                                  .safeAdd(denominator.safeSub(1))
                                  .safeDiv(denominator);
                              return partialAmount;
                          }
                          /// @dev Calculates partial value given a numerator and denominator rounded down.
                          /// @param numerator Numerator.
                          /// @param denominator Denominator.
                          /// @param target Value to calculate partial of.
                          /// @return partialAmount Partial value of target rounded down.
                          function getPartialAmountFloor(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (uint256 partialAmount)
                          {
                              partialAmount = numerator.safeMul(target).safeDiv(denominator);
                              return partialAmount;
                          }
                          /// @dev Calculates partial value given a numerator and denominator rounded down.
                          /// @param numerator Numerator.
                          /// @param denominator Denominator.
                          /// @param target Value to calculate partial of.
                          /// @return partialAmount Partial value of target rounded up.
                          function getPartialAmountCeil(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (uint256 partialAmount)
                          {
                              // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
                              //       ceil(a / b) = floor((a + b - 1) / b)
                              // To implement `ceil(a / b)` using safeDiv.
                              partialAmount = numerator.safeMul(target)
                                  .safeAdd(denominator.safeSub(1))
                                  .safeDiv(denominator);
                              return partialAmount;
                          }
                          /// @dev Checks if rounding error >= 0.1% when rounding down.
                          /// @param numerator Numerator.
                          /// @param denominator Denominator.
                          /// @param target Value to multiply with numerator/denominator.
                          /// @return isError Rounding error is present.
                          function isRoundingErrorFloor(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (bool isError)
                          {
                              if (denominator == 0) {
                                  LibRichErrorsV06.rrevert(LibMathRichErrorsV06.DivisionByZeroError());
                              }
                              // The absolute rounding error is the difference between the rounded
                              // value and the ideal value. The relative rounding error is the
                              // absolute rounding error divided by the absolute value of the
                              // ideal value. This is undefined when the ideal value is zero.
                              //
                              // The ideal value is `numerator * target / denominator`.
                              // Let's call `numerator * target % denominator` the remainder.
                              // The absolute error is `remainder / denominator`.
                              //
                              // When the ideal value is zero, we require the absolute error to
                              // be zero. Fortunately, this is always the case. The ideal value is
                              // zero iff `numerator == 0` and/or `target == 0`. In this case the
                              // remainder and absolute error are also zero.
                              if (target == 0 || numerator == 0) {
                                  return false;
                              }
                              // Otherwise, we want the relative rounding error to be strictly
                              // less than 0.1%.
                              // The relative error is `remainder / (numerator * target)`.
                              // We want the relative error less than 1 / 1000:
                              //        remainder / (numerator * denominator)  <  1 / 1000
                              // or equivalently:
                              //        1000 * remainder  <  numerator * target
                              // so we have a rounding error iff:
                              //        1000 * remainder  >=  numerator * target
                              uint256 remainder = mulmod(
                                  target,
                                  numerator,
                                  denominator
                              );
                              isError = remainder.safeMul(1000) >= numerator.safeMul(target);
                              return isError;
                          }
                          /// @dev Checks if rounding error >= 0.1% when rounding up.
                          /// @param numerator Numerator.
                          /// @param denominator Denominator.
                          /// @param target Value to multiply with numerator/denominator.
                          /// @return isError Rounding error is present.
                          function isRoundingErrorCeil(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (bool isError)
                          {
                              if (denominator == 0) {
                                  LibRichErrorsV06.rrevert(LibMathRichErrorsV06.DivisionByZeroError());
                              }
                              // See the comments in `isRoundingError`.
                              if (target == 0 || numerator == 0) {
                                  // When either is zero, the ideal value and rounded value are zero
                                  // and there is no rounding error. (Although the relative error
                                  // is undefined.)
                                  return false;
                              }
                              // Compute remainder as before
                              uint256 remainder = mulmod(
                                  target,
                                  numerator,
                                  denominator
                              );
                              remainder = denominator.safeSub(remainder) % denominator;
                              isError = remainder.safeMul(1000) >= numerator.safeMul(target);
                              return isError;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      library LibMathRichErrorsV06 {
                          // bytes4(keccak256("DivisionByZeroError()"))
                          bytes internal constant DIVISION_BY_ZERO_ERROR =
                              hex"a791837c";
                          // bytes4(keccak256("RoundingError(uint256,uint256,uint256)"))
                          bytes4 internal constant ROUNDING_ERROR_SELECTOR =
                              0x339f3de2;
                          // solhint-disable func-name-mixedcase
                          function DivisionByZeroError()
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return DIVISION_BY_ZERO_ERROR;
                          }
                          function RoundingError(
                              uint256 numerator,
                              uint256 denominator,
                              uint256 target
                          )
                              internal
                              pure
                              returns (bytes memory)
                          {
                              return abi.encodeWithSelector(
                                  ROUNDING_ERROR_SELECTOR,
                                  numerator,
                                  denominator,
                                  target
                              );
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      /// @dev Interface to the V3 Exchange.
                      interface IExchange {
                          /// @dev V3 Order structure.
                          struct Order {
                              // Address that created the order.
                              address makerAddress;
                              // Address that is allowed to fill the order.
                              // If set to 0, any address is allowed to fill the order.
                              address takerAddress;
                              // Address that will recieve fees when order is filled.
                              address feeRecipientAddress;
                              // Address that is allowed to call Exchange contract methods that affect this order.
                              // If set to 0, any address is allowed to call these methods.
                              address senderAddress;
                              // Amount of makerAsset being offered by maker. Must be greater than 0.
                              uint256 makerAssetAmount;
                              // Amount of takerAsset being bid on by maker. Must be greater than 0.
                              uint256 takerAssetAmount;
                              // Fee paid to feeRecipient by maker when order is filled.
                              uint256 makerFee;
                              // Fee paid to feeRecipient by taker when order is filled.
                              uint256 takerFee;
                              // Timestamp in seconds at which order expires.
                              uint256 expirationTimeSeconds;
                              // Arbitrary number to facilitate uniqueness of the order's hash.
                              uint256 salt;
                              // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset.
                              // The leading bytes4 references the id of the asset proxy.
                              bytes makerAssetData;
                              // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset.
                              // The leading bytes4 references the id of the asset proxy.
                              bytes takerAssetData;
                              // Encoded data that can be decoded by a specified proxy contract when transferring makerFeeAsset.
                              // The leading bytes4 references the id of the asset proxy.
                              bytes makerFeeAssetData;
                              // Encoded data that can be decoded by a specified proxy contract when transferring takerFeeAsset.
                              // The leading bytes4 references the id of the asset proxy.
                              bytes takerFeeAssetData;
                          }
                          /// @dev V3 `fillOrder()` results.`
                          struct FillResults {
                              // Total amount of makerAsset(s) filled.
                              uint256 makerAssetFilledAmount;
                              // Total amount of takerAsset(s) filled.
                              uint256 takerAssetFilledAmount;
                              // Total amount of fees paid by maker(s) to feeRecipient(s).
                              uint256 makerFeePaid;
                              // Total amount of fees paid by taker to feeRecipients(s).
                              uint256 takerFeePaid;
                              // Total amount of fees paid by taker to the staking contract.
                              uint256 protocolFeePaid;
                          }
                          /// @dev Fills the input order.
                          /// @param order Order struct containing order specifications.
                          /// @param takerAssetFillAmount Desired amount of takerAsset to sell.
                          /// @param signature Proof that order has been created by maker.
                          /// @return fillResults Amounts filled and fees paid by maker and taker.
                          function fillOrder(
                              Order calldata order,
                              uint256 takerAssetFillAmount,
                              bytes calldata signature
                          )
                              external
                              payable
                              returns (FillResults memory fillResults);
                          /// @dev Returns the protocolFeeMultiplier
                          /// @return multiplier The multiplier for protocol fees.
                          function protocolFeeMultiplier()
                              external
                              view
                              returns (uint256 multiplier);
                          /// @dev Gets an asset proxy.
                          /// @param assetProxyId Id of the asset proxy.
                          /// @return proxyAddress The asset proxy registered to assetProxyId.
                          ///         Returns 0x0 if no proxy is registered.
                          function getAssetProxy(bytes4 assetProxyId)
                              external
                              view
                              returns (address proxyAddress);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
                      import "../errors/LibTransformERC20RichErrors.sol";
                      import "./Transformer.sol";
                      import "./LibERC20Transformer.sol";
                      /// @dev A transformer that transfers tokens to the taker.
                      contract PayTakerTransformer is
                          Transformer
                      {
                          // solhint-disable no-empty-blocks
                          using LibRichErrorsV06 for bytes;
                          using LibSafeMathV06 for uint256;
                          using LibERC20Transformer for IERC20TokenV06;
                          /// @dev Transform data to ABI-encode and pass into `transform()`.
                          struct TransformData {
                              // The tokens to transfer to the taker.
                              IERC20TokenV06[] tokens;
                              // Amount of each token in `tokens` to transfer to the taker.
                              // `uint(-1)` will transfer the entire balance.
                              uint256[] amounts;
                          }
                          /// @dev Maximum uint256 value.
                          uint256 private constant MAX_UINT256 = uint256(-1);
                          /// @dev Create this contract.
                          constructor()
                              public
                              Transformer()
                          {}
                          /// @dev Forwards tokens to the taker.
                          /// @param taker The taker address (caller of `TransformERC20.transformERC20()`).
                          /// @param data_ ABI-encoded `TransformData`, indicating which tokens to transfer.
                          /// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
                          function transform(
                              bytes32, // callDataHash,
                              address payable taker,
                              bytes calldata data_
                          )
                              external
                              override
                              returns (bytes4 success)
                          {
                              TransformData memory data = abi.decode(data_, (TransformData));
                              // Transfer tokens directly to the taker.
                              for (uint256 i = 0; i < data.tokens.length; ++i) {
                                  // The `amounts` array can be shorter than the `tokens` array.
                                  // Missing elements are treated as `uint256(-1)`.
                                  uint256 amount = data.amounts.length > i ? data.amounts[i] : uint256(-1);
                                  if (amount == MAX_UINT256) {
                                      amount = data.tokens[i].getTokenBalanceOf(address(this));
                                  }
                                  if (amount != 0) {
                                      data.tokens[i].transformerTransfer(taker, amount);
                                  }
                              }
                              return LibERC20Transformer.TRANSFORMER_SUCCESS;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
                      import "../errors/LibTransformERC20RichErrors.sol";
                      import "./Transformer.sol";
                      import "./LibERC20Transformer.sol";
                      /// @dev A transformer that wraps or unwraps WETH.
                      contract WethTransformer is
                          Transformer
                      {
                          using LibRichErrorsV06 for bytes;
                          using LibSafeMathV06 for uint256;
                          using LibERC20Transformer for IERC20TokenV06;
                          /// @dev Transform data to ABI-encode and pass into `transform()`.
                          struct TransformData {
                              // The token to wrap/unwrap. Must be either ETH or WETH.
                              IERC20TokenV06 token;
                              // Amount of `token` to wrap or unwrap.
                              // `uint(-1)` will unwrap the entire balance.
                              uint256 amount;
                          }
                          /// @dev The WETH contract address.
                          IEtherTokenV06 public immutable weth;
                          /// @dev Maximum uint256 value.
                          uint256 private constant MAX_UINT256 = uint256(-1);
                          /// @dev Construct the transformer and store the WETH address in an immutable.
                          /// @param weth_ The weth token.
                          constructor(IEtherTokenV06 weth_)
                              public
                              Transformer()
                          {
                              weth = weth_;
                          }
                          /// @dev Wraps and unwraps WETH.
                          /// @param data_ ABI-encoded `TransformData`, indicating which token to wrap/umwrap.
                          /// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
                          function transform(
                              bytes32, // callDataHash,
                              address payable, // taker,
                              bytes calldata data_
                          )
                              external
                              override
                              returns (bytes4 success)
                          {
                              TransformData memory data = abi.decode(data_, (TransformData));
                              if (!data.token.isTokenETH() && data.token != weth) {
                                  LibTransformERC20RichErrors.InvalidTransformDataError(
                                      LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_TOKENS,
                                      data_
                                  ).rrevert();
                              }
                              uint256 amount = data.amount;
                              if (amount == MAX_UINT256) {
                                  amount = data.token.getTokenBalanceOf(address(this));
                              }
                              if (amount != 0) {
                                  if (data.token.isTokenETH()) {
                                      // Wrap ETH.
                                      weth.deposit{value: amount}();
                                  } else {
                                      // Unwrap WETH.
                                      weth.withdraw(amount);
                                  }
                              }
                              return LibERC20Transformer.TRANSFORMER_SUCCESS;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      import "./IERC20TokenV06.sol";
                      interface IEtherTokenV06 is
                          IERC20TokenV06
                      {
                          /// @dev Wrap ether.
                          function deposit() external payable;
                          /// @dev Unwrap ether.
                          function withdraw(uint256 amount) external;
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      interface ITestSimpleFunctionRegistryFeature {
                          function testFn() external view returns (uint256 id);
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      contract TestCallTarget {
                          event CallTargetCalled(
                              address context,
                              address sender,
                              bytes data,
                              uint256 value
                          );
                          bytes4 private constant MAGIC_BYTES = 0x12345678;
                          bytes private constant REVERTING_DATA = hex"1337";
                          fallback() external payable {
                              if (keccak256(msg.data) == keccak256(REVERTING_DATA)) {
                                  revert("TestCallTarget/REVERT");
                              }
                              emit CallTargetCalled(
                                  address(this),
                                  msg.sender,
                                  msg.data,
                                  msg.value
                              );
                              bytes4 rval = MAGIC_BYTES;
                              assembly {
                                  mstore(0, rval)
                                  return(0, 32)
                              }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      contract TestDelegateCaller {
                          function executeDelegateCall(
                              address target,
                              bytes calldata callData
                          )
                              external
                          {
                              (bool success, bytes memory resultData) = target.delegatecall(callData);
                              if (!success) {
                                  assembly { revert(add(resultData, 32), mload(resultData)) }
                              }
                              assembly { return(add(resultData, 32), mload(resultData)) }
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
                      import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
                      import "../src/vendor/v3/IExchange.sol";
                      import "./TestMintableERC20Token.sol";
                      contract TestFillQuoteTransformerExchange {
                          struct FillBehavior {
                              // How much of the order is filled, in taker asset amount.
                              uint256 filledTakerAssetAmount;
                              // Scaling for maker assets minted, in 1e18.
                              uint256 makerAssetMintRatio;
                          }
                          uint256 private constant PROTOCOL_FEE_MULTIPLIER = 1337;
                          using LibSafeMathV06 for uint256;
                          function fillOrder(
                              IExchange.Order calldata order,
                              uint256 takerAssetFillAmount,
                              bytes calldata signature
                          )
                              external
                              payable
                              returns (IExchange.FillResults memory fillResults)
                          {
                              require(
                                  signature.length != 0,
                                  "TestFillQuoteTransformerExchange/INVALID_SIGNATURE"
                              );
                              // The signature is the ABI-encoded FillBehavior data.
                              FillBehavior memory behavior = abi.decode(signature, (FillBehavior));
                              uint256 protocolFee = PROTOCOL_FEE_MULTIPLIER * tx.gasprice;
                              require(
                                  msg.value == protocolFee,
                                  "TestFillQuoteTransformerExchange/INSUFFICIENT_PROTOCOL_FEE"
                              );
                              // Return excess protocol fee.
                              msg.sender.transfer(msg.value - protocolFee);
                              // Take taker tokens.
                              TestMintableERC20Token takerToken = _getTokenFromAssetData(order.takerAssetData);
                              takerAssetFillAmount = LibSafeMathV06.min256(
                                  order.takerAssetAmount.safeSub(behavior.filledTakerAssetAmount),
                                  takerAssetFillAmount
                              );
                              require(
                                  takerToken.getSpendableAmount(msg.sender, address(this)) >= takerAssetFillAmount,
                                  "TestFillQuoteTransformerExchange/INSUFFICIENT_TAKER_FUNDS"
                              );
                              takerToken.transferFrom(msg.sender, order.makerAddress, takerAssetFillAmount);
                              // Mint maker tokens.
                              uint256 makerAssetFilledAmount = LibMathV06.getPartialAmountFloor(
                                  takerAssetFillAmount,
                                  order.takerAssetAmount,
                                  order.makerAssetAmount
                              );
                              TestMintableERC20Token makerToken = _getTokenFromAssetData(order.makerAssetData);
                              makerToken.mint(
                                  msg.sender,
                                  LibMathV06.getPartialAmountFloor(
                                      behavior.makerAssetMintRatio,
                                      1e18,
                                      makerAssetFilledAmount
                                  )
                              );
                              // Take taker fee.
                              TestMintableERC20Token takerFeeToken = _getTokenFromAssetData(order.takerFeeAssetData);
                              uint256 takerFee = LibMathV06.getPartialAmountFloor(
                                  takerAssetFillAmount,
                                  order.takerAssetAmount,
                                  order.takerFee
                              );
                              require(
                                  takerFeeToken.getSpendableAmount(msg.sender, address(this)) >= takerFee,
                                  "TestFillQuoteTransformerExchange/INSUFFICIENT_TAKER_FEE_FUNDS"
                              );
                              takerFeeToken.transferFrom(msg.sender, order.feeRecipientAddress, takerFee);
                              fillResults.makerAssetFilledAmount = makerAssetFilledAmount;
                              fillResults.takerAssetFilledAmount = takerAssetFillAmount;
                              fillResults.makerFeePaid = uint256(-1);
                              fillResults.takerFeePaid = takerFee;
                              fillResults.protocolFeePaid = protocolFee;
                          }
                          function encodeBehaviorData(FillBehavior calldata behavior)
                              external
                              pure
                              returns (bytes memory encoded)
                          {
                              return abi.encode(behavior);
                          }
                          function protocolFeeMultiplier()
                              external
                              pure
                              returns (uint256)
                          {
                              return PROTOCOL_FEE_MULTIPLIER;
                          }
                          function getAssetProxy(bytes4)
                              external
                              view
                              returns (address)
                          {
                              return address(this);
                          }
                          function _getTokenFromAssetData(bytes memory assetData)
                              private
                              pure
                              returns (TestMintableERC20Token token)
                          {
                              return TestMintableERC20Token(LibBytesV06.readAddress(assetData, 16));
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      contract TestMintableERC20Token {
                          mapping(address => uint256) public balanceOf;
                          mapping(address => mapping(address => uint256)) public allowance;
                          function transfer(address to, uint256 amount)
                              external
                              virtual
                              returns (bool)
                          {
                              return transferFrom(msg.sender, to, amount);
                          }
                          function approve(address spender, uint256 amount)
                              external
                              virtual
                              returns (bool)
                          {
                              allowance[msg.sender][spender] = amount;
                              return true;
                          }
                          function mint(address owner, uint256 amount)
                              external
                              virtual
                          {
                              balanceOf[owner] += amount;
                          }
                          function burn(address owner, uint256 amount)
                              external
                              virtual
                          {
                              require(balanceOf[owner] >= amount, "TestMintableERC20Token/INSUFFICIENT_FUNDS");
                              balanceOf[owner] -= amount;
                          }
                          function transferFrom(address from, address to, uint256 amount)
                              public
                              virtual
                              returns (bool)
                          {
                              if (from != msg.sender) {
                                  require(
                                      allowance[from][msg.sender] >= amount,
                                      "TestMintableERC20Token/INSUFFICIENT_ALLOWANCE"
                                  );
                                  allowance[from][msg.sender] -= amount;
                              }
                              require(balanceOf[from] >= amount, "TestMintableERC20Token/INSUFFICIENT_FUNDS");
                              balanceOf[from] -= amount;
                              balanceOf[to] += amount;
                              return true;
                          }
                          function getSpendableAmount(address owner, address spender)
                              external
                              view
                              returns (uint256)
                          {
                              return balanceOf[owner] < allowance[owner][spender]
                                  ? balanceOf[owner]
                                  : allowance[owner][spender];
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/transformers/IERC20Transformer.sol";
                      import "./TestMintableERC20Token.sol";
                      import "./TestTransformerHost.sol";
                      contract TestFillQuoteTransformerHost is
                          TestTransformerHost
                      {
                          function executeTransform(
                              IERC20Transformer transformer,
                              TestMintableERC20Token inputToken,
                              uint256 inputTokenAmount,
                              bytes calldata data
                          )
                              external
                              payable
                          {
                              if (inputTokenAmount != 0) {
                                  inputToken.mint(address(this), inputTokenAmount);
                              }
                              // Have to make this call externally because transformers aren't payable.
                              this.rawExecuteTransform(transformer, bytes32(0), msg.sender, data);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "../src/transformers/IERC20Transformer.sol";
                      import "../src/transformers/LibERC20Transformer.sol";
                      contract TestTransformerHost {
                          using LibERC20Transformer for IERC20TokenV06;
                          using LibRichErrorsV06 for bytes;
                          function rawExecuteTransform(
                              IERC20Transformer transformer,
                              bytes32 callDataHash,
                              address taker,
                              bytes calldata data
                          )
                              external
                          {
                              (bool _success, bytes memory resultData) =
                                  address(transformer).delegatecall(abi.encodeWithSelector(
                                      transformer.transform.selector,
                                      callDataHash,
                                      taker,
                                      data
                                  ));
                              if (!_success) {
                                  resultData.rrevert();
                              }
                              require(
                                  abi.decode(resultData, (bytes4)) == LibERC20Transformer.TRANSFORMER_SUCCESS,
                                  "TestTransformerHost/INVALID_TRANSFORMER_RESULT"
                              );
                          }
                          // solhint-disable
                          receive() external payable {}
                          // solhint-enable
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/ZeroEx.sol";
                      import "../src/features/IBootstrap.sol";
                      import "../src/migrations/FullMigration.sol";
                      contract TestFullMigration is
                          FullMigration
                      {
                          address public dieRecipient;
                          // solhint-disable-next-line no-empty-blocks
                          constructor(address payable deployer) public FullMigration(deployer) {}
                          function die(address payable ethRecipient) external override {
                              dieRecipient = ethRecipient;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/ZeroEx.sol";
                      import "../src/features/IBootstrap.sol";
                      import "../src/migrations/InitialMigration.sol";
                      contract TestInitialMigration is
                          InitialMigration
                      {
                          address public bootstrapFeature;
                          address public dieRecipient;
                          // solhint-disable-next-line no-empty-blocks
                          constructor(address deployer) public InitialMigration(deployer) {}
                          function callBootstrap(ZeroEx zeroEx) external {
                              IBootstrap(address(zeroEx)).bootstrap(address(this), new bytes(0));
                          }
                          function bootstrap(address owner, BootstrapFeatures memory features)
                              public
                              override
                              returns (bytes4 success)
                          {
                              success = InitialMigration.bootstrap(owner, features);
                              // Snoop the bootstrap feature contract.
                              bootstrapFeature = ZeroEx(address(uint160(address(this))))
                                  .getFunctionImplementation(IBootstrap.bootstrap.selector);
                          }
                          function die(address payable ethRecipient) public override {
                              dieRecipient = ethRecipient;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/migrations/LibMigrate.sol";
                      import "../src/features/IOwnable.sol";
                      contract TestMigrator {
                          event TestMigrateCalled(
                              bytes callData,
                              address owner
                          );
                          function succeedingMigrate() external returns (bytes4 success) {
                              emit TestMigrateCalled(
                                  msg.data,
                                  IOwnable(address(this)).owner()
                              );
                              return LibMigrate.MIGRATE_SUCCESS;
                          }
                          function failingMigrate() external returns (bytes4 success) {
                              emit TestMigrateCalled(
                                  msg.data,
                                  IOwnable(address(this)).owner()
                              );
                              return 0xdeadbeef;
                          }
                          function revertingMigrate() external pure {
                              revert("OOPSIE");
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
                      import "../src/transformers/IERC20Transformer.sol";
                      import "../src/transformers/LibERC20Transformer.sol";
                      import "./TestMintableERC20Token.sol";
                      contract TestMintTokenERC20Transformer is
                          IERC20Transformer
                      {
                          struct TransformData {
                              IERC20TokenV06 inputToken;
                              TestMintableERC20Token outputToken;
                              uint256 burnAmount;
                              uint256 mintAmount;
                              uint256 feeAmount;
                          }
                          event MintTransform(
                              address context,
                              address caller,
                              bytes32 callDataHash,
                              address taker,
                              bytes data,
                              uint256 inputTokenBalance,
                              uint256 ethBalance
                          );
                          function transform(
                              bytes32 callDataHash,
                              address payable taker,
                              bytes calldata data_
                          )
                              external
                              override
                              returns (bytes4 success)
                          {
                              TransformData memory data = abi.decode(data_, (TransformData));
                              emit MintTransform(
                                  address(this),
                                  msg.sender,
                                  callDataHash,
                                  taker,
                                  data_,
                                  data.inputToken.balanceOf(address(this)),
                                  address(this).balance
                              );
                              // "Burn" input tokens.
                              data.inputToken.transfer(address(0), data.burnAmount);
                              // Mint output tokens.
                              if (LibERC20Transformer.isTokenETH(IERC20TokenV06(address(data.outputToken)))) {
                                  taker.transfer(data.mintAmount);
                              } else {
                                  data.outputToken.mint(
                                      taker,
                                      data.mintAmount
                                  );
                                  // Burn fees from output.
                                  data.outputToken.burn(taker, data.feeAmount);
                              }
                              return LibERC20Transformer.TRANSFORMER_SUCCESS;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/fixins/FixinCommon.sol";
                      contract TestSimpleFunctionRegistryFeatureImpl1 is
                          FixinCommon
                      {
                          function testFn()
                              external
                              pure
                              returns (uint256 id)
                          {
                              return 1337;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/fixins/FixinCommon.sol";
                      contract TestSimpleFunctionRegistryFeatureImpl2 is
                          FixinCommon
                      {
                          function testFn()
                              external
                              pure
                              returns (uint256 id)
                          {
                              return 1338;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/features/TokenSpender.sol";
                      contract TestTokenSpender is
                          TokenSpender
                      {
                          modifier onlySelf() override {
                              _;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./TestMintableERC20Token.sol";
                      contract TestTokenSpenderERC20Token is
                          TestMintableERC20Token
                      {
                          event TransferFromCalled(
                              address sender,
                              address from,
                              address to,
                              uint256 amount
                          );
                          // `transferFrom()` behavior depends on the value of `amount`.
                          uint256 constant private EMPTY_RETURN_AMOUNT = 1337;
                          uint256 constant private FALSE_RETURN_AMOUNT = 1338;
                          uint256 constant private REVERT_RETURN_AMOUNT = 1339;
                          function transferFrom(address from, address to, uint256 amount)
                              public
                              override
                              returns (bool)
                          {
                              emit TransferFromCalled(msg.sender, from, to, amount);
                              if (amount == EMPTY_RETURN_AMOUNT) {
                                  assembly { return(0, 0) }
                              }
                              if (amount == FALSE_RETURN_AMOUNT) {
                                  return false;
                              }
                              if (amount == REVERT_RETURN_AMOUNT) {
                                  revert("TestTokenSpenderERC20Token/Revert");
                              }
                              return true;
                          }
                          function setBalanceAndAllowanceOf(
                              address owner,
                              uint256 balance,
                              address spender,
                              uint256 allowance_
                          )
                              external
                          {
                              balanceOf[owner] = balance;
                              allowance[owner][spender] = allowance_;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/features/TransformERC20.sol";
                      contract TestTransformERC20 is
                          TransformERC20
                      {
                          // solhint-disable no-empty-blocks
                          constructor()
                              TransformERC20()
                              public
                          {}
                          modifier onlySelf() override {
                              _;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/transformers/Transformer.sol";
                      import "../src/transformers/LibERC20Transformer.sol";
                      contract TestTransformerBase is
                          Transformer
                      {
                          function transform(
                              bytes32,
                              address payable,
                              bytes calldata
                          )
                              external
                              override
                              returns (bytes4 success)
                          {
                              return LibERC20Transformer.TRANSFORMER_SUCCESS;
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/transformers/LibERC20Transformer.sol";
                      contract TestTransformerDeployerTransformer {
                          address payable public immutable deployer;
                          constructor() public payable {
                              deployer = msg.sender;
                          }
                          modifier onlyDeployer() {
                              require(msg.sender == deployer, "TestTransformerDeployerTransformer/ONLY_DEPLOYER");
                              _;
                          }
                          function die()
                              external
                              onlyDeployer
                          {
                              selfdestruct(deployer);
                          }
                          function isDeployedByDeployer(uint32 nonce)
                              external
                              view
                              returns (bool)
                          {
                              return LibERC20Transformer.getDeployedAddress(deployer, nonce) == address(this);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "./TestMintableERC20Token.sol";
                      contract TestWeth is
                          TestMintableERC20Token
                      {
                          function deposit()
                              external
                              payable
                          {
                              this.mint(msg.sender, msg.value);
                          }
                          function withdraw(uint256 amount)
                              external
                          {
                              require(balanceOf[msg.sender] >= amount, "TestWeth/INSUFFICIENT_FUNDS");
                              balanceOf[msg.sender] -= amount;
                              msg.sender.transfer(amount);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/transformers/IERC20Transformer.sol";
                      import "./TestMintableERC20Token.sol";
                      import "./TestTransformerHost.sol";
                      import "./TestWeth.sol";
                      contract TestWethTransformerHost is
                          TestTransformerHost
                      {
                          // solhint-disable
                          TestWeth private immutable _weth;
                          // solhint-enable
                          constructor(TestWeth weth) public {
                              _weth = weth;
                          }
                          function executeTransform(
                              uint256 wethAmount,
                              IERC20Transformer transformer,
                              bytes calldata data
                          )
                              external
                              payable
                          {
                              if (wethAmount != 0) {
                                  _weth.deposit{value: wethAmount}();
                              }
                              // Have to make this call externally because transformers aren't payable.
                              this.rawExecuteTransform(transformer, bytes32(0), msg.sender, data);
                          }
                      }
                      /*
                        Copyright 2020 ZeroEx Intl.
                        Licensed under the Apache License, Version 2.0 (the "License");
                        you may not use this file except in compliance with the License.
                        You may obtain a copy of the License at
                          http://www.apache.org/licenses/LICENSE-2.0
                        Unless required by applicable law or agreed to in writing, software
                        distributed under the License is distributed on an "AS IS" BASIS,
                        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                        See the License for the specific language governing permissions and
                        limitations under the License.
                      */
                      pragma solidity ^0.6.5;
                      pragma experimental ABIEncoderV2;
                      import "../src/fixins/FixinCommon.sol";
                      import "../src/ZeroEx.sol";
                      contract TestZeroExFeature is
                          FixinCommon
                      {
                          event PayableFnCalled(uint256 value);
                          event NotPayableFnCalled();
                          function payableFn()
                              external
                              payable
                          {
                              emit PayableFnCalled(msg.value);
                          }
                          function notPayableFn()
                              external
                          {
                              emit NotPayableFnCalled();
                          }
                          // solhint-disable no-empty-blocks
                          function unimplmentedFn()
                              external
                          {}
                          function internalFn()
                              external
                              onlySelf
                          {}
                      }
                      

                      File 7 of 12: SocketGateway
                      // SPDX-License-Identifier: AGPL-3.0-only
                      pragma solidity >=0.8.0;
                      /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
                      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
                      /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
                      /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
                      abstract contract ERC20 {
                          /*//////////////////////////////////////////////////////////////
                                                       EVENTS
                          //////////////////////////////////////////////////////////////*/
                          event Transfer(address indexed from, address indexed to, uint256 amount);
                          event Approval(address indexed owner, address indexed spender, uint256 amount);
                          /*//////////////////////////////////////////////////////////////
                                                  METADATA STORAGE
                          //////////////////////////////////////////////////////////////*/
                          string public name;
                          string public symbol;
                          uint8 public immutable decimals;
                          /*//////////////////////////////////////////////////////////////
                                                    ERC20 STORAGE
                          //////////////////////////////////////////////////////////////*/
                          uint256 public totalSupply;
                          mapping(address => uint256) public balanceOf;
                          mapping(address => mapping(address => uint256)) public allowance;
                          /*//////////////////////////////////////////////////////////////
                                                  EIP-2612 STORAGE
                          //////////////////////////////////////////////////////////////*/
                          uint256 internal immutable INITIAL_CHAIN_ID;
                          bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
                          mapping(address => uint256) public nonces;
                          /*//////////////////////////////////////////////////////////////
                                                     CONSTRUCTOR
                          //////////////////////////////////////////////////////////////*/
                          constructor(
                              string memory _name,
                              string memory _symbol,
                              uint8 _decimals
                          ) {
                              name = _name;
                              symbol = _symbol;
                              decimals = _decimals;
                              INITIAL_CHAIN_ID = block.chainid;
                              INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
                          }
                          /*//////////////////////////////////////////////////////////////
                                                     ERC20 LOGIC
                          //////////////////////////////////////////////////////////////*/
                          function approve(address spender, uint256 amount) public virtual returns (bool) {
                              allowance[msg.sender][spender] = amount;
                              emit Approval(msg.sender, spender, amount);
                              return true;
                          }
                          function transfer(address to, uint256 amount) public virtual returns (bool) {
                              balanceOf[msg.sender] -= amount;
                              // Cannot overflow because the sum of all user
                              // balances can't exceed the max uint256 value.
                              unchecked {
                                  balanceOf[to] += amount;
                              }
                              emit Transfer(msg.sender, to, amount);
                              return true;
                          }
                          function transferFrom(
                              address from,
                              address to,
                              uint256 amount
                          ) public virtual returns (bool) {
                              uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
                              if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
                              balanceOf[from] -= amount;
                              // Cannot overflow because the sum of all user
                              // balances can't exceed the max uint256 value.
                              unchecked {
                                  balanceOf[to] += amount;
                              }
                              emit Transfer(from, to, amount);
                              return true;
                          }
                          /*//////////////////////////////////////////////////////////////
                                                   EIP-2612 LOGIC
                          //////////////////////////////////////////////////////////////*/
                          function permit(
                              address owner,
                              address spender,
                              uint256 value,
                              uint256 deadline,
                              uint8 v,
                              bytes32 r,
                              bytes32 s
                          ) public virtual {
                              require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
                              // Unchecked because the only math done is incrementing
                              // the owner's nonce which cannot realistically overflow.
                              unchecked {
                                  address recoveredAddress = ecrecover(
                                      keccak256(
                                          abi.encodePacked(
                                              "\\x19\\x01",
                                              DOMAIN_SEPARATOR(),
                                              keccak256(
                                                  abi.encode(
                                                      keccak256(
                                                          "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                                      ),
                                                      owner,
                                                      spender,
                                                      value,
                                                      nonces[owner]++,
                                                      deadline
                                                  )
                                              )
                                          )
                                      ),
                                      v,
                                      r,
                                      s
                                  );
                                  require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
                                  allowance[recoveredAddress][spender] = value;
                              }
                              emit Approval(owner, spender, value);
                          }
                          function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
                              return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
                          }
                          function computeDomainSeparator() internal view virtual returns (bytes32) {
                              return
                                  keccak256(
                                      abi.encode(
                                          keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                                          keccak256(bytes(name)),
                                          keccak256("1"),
                                          block.chainid,
                                          address(this)
                                      )
                                  );
                          }
                          /*//////////////////////////////////////////////////////////////
                                              INTERNAL MINT/BURN LOGIC
                          //////////////////////////////////////////////////////////////*/
                          function _mint(address to, uint256 amount) internal virtual {
                              totalSupply += amount;
                              // Cannot overflow because the sum of all user
                              // balances can't exceed the max uint256 value.
                              unchecked {
                                  balanceOf[to] += amount;
                              }
                              emit Transfer(address(0), to, amount);
                          }
                          function _burn(address from, uint256 amount) internal virtual {
                              balanceOf[from] -= amount;
                              // Cannot underflow because a user's balance
                              // will never be larger than the total supply.
                              unchecked {
                                  totalSupply -= amount;
                              }
                              emit Transfer(from, address(0), amount);
                          }
                      }
                      // SPDX-License-Identifier: AGPL-3.0-only
                      pragma solidity >=0.8.0;
                      import {ERC20} from "../tokens/ERC20.sol";
                      /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
                      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
                      /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
                      /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
                      library SafeTransferLib {
                          /*//////////////////////////////////////////////////////////////
                                                   ETH OPERATIONS
                          //////////////////////////////////////////////////////////////*/
                          function safeTransferETH(address to, uint256 amount) internal {
                              bool success;
                              /// @solidity memory-safe-assembly
                              assembly {
                                  // Transfer the ETH and store if it succeeded or not.
                                  success := call(gas(), to, amount, 0, 0, 0, 0)
                              }
                              require(success, "ETH_TRANSFER_FAILED");
                          }
                          /*//////////////////////////////////////////////////////////////
                                                  ERC20 OPERATIONS
                          //////////////////////////////////////////////////////////////*/
                          function safeTransferFrom(
                              ERC20 token,
                              address from,
                              address to,
                              uint256 amount
                          ) internal {
                              bool success;
                              /// @solidity memory-safe-assembly
                              assembly {
                                  // Get a pointer to some free memory.
                                  let freeMemoryPointer := mload(0x40)
                                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                                  mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
                                  mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
                                  mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
                                  mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.
                                  success := and(
                                      // Set success to whether the call reverted, if not we check it either
                                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                                      // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                      // Counterintuitively, this call must be positioned second to the or() call in the
                                      // surrounding and() call or else returndatasize() will be zero during the computation.
                                      call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
                                  )
                              }
                              require(success, "TRANSFER_FROM_FAILED");
                          }
                          function safeTransfer(
                              ERC20 token,
                              address to,
                              uint256 amount
                          ) internal {
                              bool success;
                              /// @solidity memory-safe-assembly
                              assembly {
                                  // Get a pointer to some free memory.
                                  let freeMemoryPointer := mload(0x40)
                                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                                  mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
                                  mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
                                  mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
                                  success := and(
                                      // Set success to whether the call reverted, if not we check it either
                                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                                      // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                      // Counterintuitively, this call must be positioned second to the or() call in the
                                      // surrounding and() call or else returndatasize() will be zero during the computation.
                                      call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                                  )
                              }
                              require(success, "TRANSFER_FAILED");
                          }
                          function safeApprove(
                              ERC20 token,
                              address to,
                              uint256 amount
                          ) internal {
                              bool success;
                              /// @solidity memory-safe-assembly
                              assembly {
                                  // Get a pointer to some free memory.
                                  let freeMemoryPointer := mload(0x40)
                                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                                  mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
                                  mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
                                  mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
                                  success := and(
                                      // Set success to whether the call reverted, if not we check it either
                                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                                      // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                      // Counterintuitively, this call must be positioned second to the or() call in the
                                      // surrounding and() call or else returndatasize() will be zero during the computation.
                                      call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                                  )
                              }
                              require(success, "APPROVE_FAILED");
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import "./interfaces/across.sol";
                      import "../BridgeImplBase.sol";
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {ACROSS} from "../../static/RouteIdentifiers.sol";
                      /**
                       * @title Across-Route Implementation
                       * @notice Route implementation with functions to bridge ERC20 and Native via Across-Bridge
                       * Called via SocketGateway if the routeId in the request maps to the routeId of AcrossImplementation
                       * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap
                       * RequestData is different to just bride and bridging chained with swap
                       * @author Socket dot tech.
                       */
                      contract AcrossImpl is BridgeImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable AcrossIdentifier = ACROSS;
                          /// @notice Function-selector for ERC20-token bridging on Across-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens
                          bytes4 public immutable ACROSS_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeERC20To(uint256,uint256,bytes32,address,address,uint32,uint64)"
                                  )
                              );
                          /// @notice Function-selector for Native bridging on Across-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens
                          bytes4 public immutable ACROSS_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeNativeTo(uint256,uint256,bytes32,address,uint32,uint64)"
                                  )
                              );
                          bytes4 public immutable ACROSS_SWAP_BRIDGE_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "swapAndBridge(uint32,bytes,(uint256,address,uint32,uint64,bytes32))"
                                  )
                              );
                          /// @notice spokePool Contract instance used to deposit ERC20 and Native on to Across-Bridge
                          /// @dev contract instance is to be initialized in the constructor using the spokePoolAddress passed as constructor argument
                          SpokePool public immutable spokePool;
                          address public immutable spokePoolAddress;
                          /// @notice address of WETH token to be initialised in constructor
                          address public immutable WETH;
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct AcrossBridgeDataNoToken {
                              uint256 toChainId;
                              address receiverAddress;
                              uint32 quoteTimestamp;
                              uint64 relayerFeePct;
                              bytes32 metadata;
                          }
                          struct AcrossBridgeData {
                              uint256 toChainId;
                              address receiverAddress;
                              address token;
                              uint32 quoteTimestamp;
                              uint64 relayerFeePct;
                              bytes32 metadata;
                          }
                          /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase
                          /// @dev ensure spokepool, weth-address are set properly for the chainId in which the contract is being deployed
                          constructor(
                              address _spokePool,
                              address _wethAddress,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) BridgeImplBase(_socketGateway, _socketDeployFactory) {
                              spokePool = SpokePool(_spokePool);
                              spokePoolAddress = _spokePool;
                              WETH = _wethAddress;
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in AcrossBridgeData struct
                           * @param amount amount of tokens being bridged. this can be ERC20 or native
                           * @param bridgeData encoded data for AcrossBridge
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable override {
                              AcrossBridgeData memory acrossBridgeData = abi.decode(
                                  bridgeData,
                                  (AcrossBridgeData)
                              );
                              if (acrossBridgeData.token == NATIVE_TOKEN_ADDRESS) {
                                  spokePool.deposit{value: amount}(
                                      acrossBridgeData.receiverAddress,
                                      WETH,
                                      amount,
                                      acrossBridgeData.toChainId,
                                      acrossBridgeData.relayerFeePct,
                                      acrossBridgeData.quoteTimestamp
                                  );
                              } else {
                                  spokePool.deposit(
                                      acrossBridgeData.receiverAddress,
                                      acrossBridgeData.token,
                                      amount,
                                      acrossBridgeData.toChainId,
                                      acrossBridgeData.relayerFeePct,
                                      acrossBridgeData.quoteTimestamp
                                  );
                              }
                              emit SocketBridge(
                                  amount,
                                  acrossBridgeData.token,
                                  acrossBridgeData.toChainId,
                                  AcrossIdentifier,
                                  msg.sender,
                                  acrossBridgeData.receiverAddress,
                                  acrossBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in AcrossBridgeData struct
                           * @param swapId routeId for the swapImpl
                           * @param swapData encoded data for swap
                           * @param acrossBridgeData encoded data for AcrossBridge
                           */
                          function swapAndBridge(
                              uint32 swapId,
                              bytes calldata swapData,
                              AcrossBridgeDataNoToken calldata acrossBridgeData
                          ) external payable {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(swapId)
                                  .delegatecall(swapData);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              (uint256 bridgeAmount, address token) = abi.decode(
                                  result,
                                  (uint256, address)
                              );
                              if (token == NATIVE_TOKEN_ADDRESS) {
                                  spokePool.deposit{value: bridgeAmount}(
                                      acrossBridgeData.receiverAddress,
                                      WETH,
                                      bridgeAmount,
                                      acrossBridgeData.toChainId,
                                      acrossBridgeData.relayerFeePct,
                                      acrossBridgeData.quoteTimestamp
                                  );
                              } else {
                                  spokePool.deposit(
                                      acrossBridgeData.receiverAddress,
                                      token,
                                      bridgeAmount,
                                      acrossBridgeData.toChainId,
                                      acrossBridgeData.relayerFeePct,
                                      acrossBridgeData.quoteTimestamp
                                  );
                              }
                              emit SocketBridge(
                                  bridgeAmount,
                                  token,
                                  acrossBridgeData.toChainId,
                                  AcrossIdentifier,
                                  msg.sender,
                                  acrossBridgeData.receiverAddress,
                                  acrossBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to handle ERC20 bridging to receipent via Across-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param amount amount being bridged
                           * @param toChainId destination ChainId
                           * @param receiverAddress address of receiver of bridged tokens
                           * @param token address of token being bridged
                           * @param quoteTimestamp timestamp for quote and this is to be used by Across-Bridge contract
                           * @param relayerFeePct feePct that will be relayed by the Bridge to the relayer
                           */
                          function bridgeERC20To(
                              uint256 amount,
                              uint256 toChainId,
                              bytes32 metadata,
                              address receiverAddress,
                              address token,
                              uint32 quoteTimestamp,
                              uint64 relayerFeePct
                          ) external payable {
                              ERC20 tokenInstance = ERC20(token);
                              tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);
                              spokePool.deposit(
                                  receiverAddress,
                                  address(token),
                                  amount,
                                  toChainId,
                                  relayerFeePct,
                                  quoteTimestamp
                              );
                              emit SocketBridge(
                                  amount,
                                  token,
                                  toChainId,
                                  AcrossIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                          /**
                           * @notice function to handle Native bridging to receipent via Across-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param amount amount being bridged
                           * @param toChainId destination ChainId
                           * @param receiverAddress address of receiver of bridged tokens
                           * @param quoteTimestamp timestamp for quote and this is to be used by Across-Bridge contract
                           * @param relayerFeePct feePct that will be relayed by the Bridge to the relayer
                           */
                          function bridgeNativeTo(
                              uint256 amount,
                              uint256 toChainId,
                              bytes32 metadata,
                              address receiverAddress,
                              uint32 quoteTimestamp,
                              uint64 relayerFeePct
                          ) external payable {
                              spokePool.deposit{value: amount}(
                                  receiverAddress,
                                  WETH,
                                  amount,
                                  toChainId,
                                  relayerFeePct,
                                  quoteTimestamp
                              );
                              emit SocketBridge(
                                  amount,
                                  NATIVE_TOKEN_ADDRESS,
                                  toChainId,
                                  AcrossIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.0;
                      /// @notice interface with functions to interact with SpokePool contract of Across-Bridge
                      interface SpokePool {
                          /**************************************
                           *         DEPOSITOR FUNCTIONS        *
                           **************************************/
                          /**
                           * @notice Called by user to bridge funds from origin to destination chain. Depositor will effectively lock
                           * tokens in this contract and receive a destination token on the destination chain. The origin => destination
                           * token mapping is stored on the L1 HubPool.
                           * @notice The caller must first approve this contract to spend amount of originToken.
                           * @notice The originToken => destinationChainId must be enabled.
                           * @notice This method is payable because the caller is able to deposit native token if the originToken is
                           * wrappedNativeToken and this function will handle wrapping the native token to wrappedNativeToken.
                           * @param recipient Address to receive funds at on destination chain.
                           * @param originToken Token to lock into this contract to initiate deposit.
                           * @param amount Amount of tokens to deposit. Will be amount of tokens to receive less fees.
                           * @param destinationChainId Denotes network where user will receive funds from SpokePool by a relayer.
                           * @param relayerFeePct % of deposit amount taken out to incentivize a fast relayer.
                           * @param quoteTimestamp Timestamp used by relayers to compute this deposit's realizedLPFeePct which is paid
                           * to LP pool on HubPool.
                           */
                          function deposit(
                              address recipient,
                              address originToken,
                              uint256 amount,
                              uint256 destinationChainId,
                              uint64 relayerFeePct,
                              uint32 quoteTimestamp
                          ) external payable;
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {BridgeImplBase} from "../../BridgeImplBase.sol";
                      import {ANYSWAP} from "../../../static/RouteIdentifiers.sol";
                      /**
                       * @title Anyswap-V4-Route L1 Implementation
                       * @notice Route implementation with functions to bridge ERC20 via Anyswap-Bridge
                       * Called via SocketGateway if the routeId in the request maps to the routeId of AnyswapImplementation
                       * This is the L1 implementation, so this is used when transferring from l1 to supported l1s or L1.
                       * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap
                       * RequestData is different to just bride and bridging chained with swap
                       * @author Socket dot tech.
                       */
                      /// @notice Interface to interact with AnyswapV4-Router Implementation
                      interface AnyswapV4Router {
                          function anySwapOutUnderlying(
                              address token,
                              address to,
                              uint256 amount,
                              uint256 toChainID
                          ) external;
                      }
                      contract AnyswapImplL1 is BridgeImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable AnyswapIdentifier = ANYSWAP;
                          /// @notice Function-selector for ERC20-token bridging on Anyswap-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens
                          bytes4 public immutable ANYSWAP_L1_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeERC20To(uint256,uint256,bytes32,address,address,address)"
                                  )
                              );
                          bytes4 public immutable ANYSWAP_SWAP_BRIDGE_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "swapAndBridge(uint32,bytes,(uint256,address,address,bytes32))"
                                  )
                              );
                          /// @notice AnSwapV4Router Contract instance used to deposit ERC20 on to Anyswap-Bridge
                          /// @dev contract instance is to be initialized in the constructor using the router-address passed as constructor argument
                          AnyswapV4Router public immutable router;
                          /**
                           * @notice Constructor sets the router address and socketGateway address.
                           * @dev anyswap 4 router is immutable. so no setter function required.
                           */
                          constructor(
                              address _router,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) BridgeImplBase(_socketGateway, _socketDeployFactory) {
                              router = AnyswapV4Router(_router);
                          }
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct AnyswapBridgeDataNoToken {
                              /// @notice destination ChainId
                              uint256 toChainId;
                              /// @notice address of receiver of bridged tokens
                              address receiverAddress;
                              /// @notice address of wrapperToken, WrappedVersion of the token being bridged
                              address wrapperTokenAddress;
                              /// @notice socket offchain created hash
                              bytes32 metadata;
                          }
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct AnyswapBridgeData {
                              /// @notice destination ChainId
                              uint256 toChainId;
                              /// @notice address of receiver of bridged tokens
                              address receiverAddress;
                              /// @notice address of wrapperToken, WrappedVersion of the token being bridged
                              address wrapperTokenAddress;
                              /// @notice address of token being bridged
                              address token;
                              /// @notice socket offchain created hash
                              bytes32 metadata;
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct
                           * @param amount amount of tokens being bridged. this can be ERC20 or native
                           * @param bridgeData encoded data for AnyswapBridge
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable override {
                              AnyswapBridgeData memory anyswapBridgeData = abi.decode(
                                  bridgeData,
                                  (AnyswapBridgeData)
                              );
                              ERC20(anyswapBridgeData.token).safeApprove(address(router), amount);
                              router.anySwapOutUnderlying(
                                  anyswapBridgeData.wrapperTokenAddress,
                                  anyswapBridgeData.receiverAddress,
                                  amount,
                                  anyswapBridgeData.toChainId
                              );
                              emit SocketBridge(
                                  amount,
                                  anyswapBridgeData.token,
                                  anyswapBridgeData.toChainId,
                                  AnyswapIdentifier,
                                  msg.sender,
                                  anyswapBridgeData.receiverAddress,
                                  anyswapBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct
                           * @param swapId routeId for the swapImpl
                           * @param swapData encoded data for swap
                           * @param anyswapBridgeData encoded data for AnyswapBridge
                           */
                          function swapAndBridge(
                              uint32 swapId,
                              bytes calldata swapData,
                              AnyswapBridgeDataNoToken calldata anyswapBridgeData
                          ) external payable {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(swapId)
                                  .delegatecall(swapData);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              (uint256 bridgeAmount, address token) = abi.decode(
                                  result,
                                  (uint256, address)
                              );
                              ERC20(token).safeApprove(address(router), bridgeAmount);
                              router.anySwapOutUnderlying(
                                  anyswapBridgeData.wrapperTokenAddress,
                                  anyswapBridgeData.receiverAddress,
                                  bridgeAmount,
                                  anyswapBridgeData.toChainId
                              );
                              emit SocketBridge(
                                  bridgeAmount,
                                  token,
                                  anyswapBridgeData.toChainId,
                                  AnyswapIdentifier,
                                  msg.sender,
                                  anyswapBridgeData.receiverAddress,
                                  anyswapBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to handle ERC20 bridging to receipent via Anyswap-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param amount amount being bridged
                           * @param toChainId destination ChainId
                           * @param receiverAddress address of receiver of bridged tokens
                           * @param token address of token being bridged
                           * @param wrapperTokenAddress address of wrapperToken, WrappedVersion of the token being bridged
                           */
                          function bridgeERC20To(
                              uint256 amount,
                              uint256 toChainId,
                              bytes32 metadata,
                              address receiverAddress,
                              address token,
                              address wrapperTokenAddress
                          ) external payable {
                              ERC20 tokenInstance = ERC20(token);
                              tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);
                              tokenInstance.safeApprove(address(router), amount);
                              router.anySwapOutUnderlying(
                                  wrapperTokenAddress,
                                  receiverAddress,
                                  amount,
                                  toChainId
                              );
                              emit SocketBridge(
                                  amount,
                                  token,
                                  toChainId,
                                  AnyswapIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {BridgeImplBase} from "../../BridgeImplBase.sol";
                      import {ANYSWAP} from "../../../static/RouteIdentifiers.sol";
                      /**
                       * @title Anyswap-V4-Route L1 Implementation
                       * @notice Route implementation with functions to bridge ERC20 via Anyswap-Bridge
                       * Called via SocketGateway if the routeId in the request maps to the routeId of AnyswapImplementation
                       * This is the L2 implementation, so this is used when transferring from l2.
                       * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap
                       * RequestData is different to just bride and bridging chained with swap
                       * @author Socket dot tech.
                       */
                      interface AnyswapV4Router {
                          function anySwapOutUnderlying(
                              address token,
                              address to,
                              uint256 amount,
                              uint256 toChainID
                          ) external;
                      }
                      contract AnyswapL2Impl is BridgeImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable AnyswapIdentifier = ANYSWAP;
                          /// @notice Function-selector for ERC20-token bridging on Anyswap-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens
                          bytes4 public immutable ANYSWAP_L2_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeERC20To(uint256,uint256,bytes32,address,address,address)"
                                  )
                              );
                          bytes4 public immutable ANYSWAP_SWAP_BRIDGE_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "swapAndBridge(uint32,bytes,(uint256,address,address,bytes32))"
                                  )
                              );
                          // polygon router multichain router v4
                          AnyswapV4Router public immutable router;
                          /**
                           * @notice Constructor sets the router address and socketGateway address.
                           * @dev anyswap v4 router is immutable. so no setter function required.
                           */
                          constructor(
                              address _router,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) BridgeImplBase(_socketGateway, _socketDeployFactory) {
                              router = AnyswapV4Router(_router);
                          }
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct AnyswapBridgeDataNoToken {
                              /// @notice destination ChainId
                              uint256 toChainId;
                              /// @notice address of receiver of bridged tokens
                              address receiverAddress;
                              /// @notice address of wrapperToken, WrappedVersion of the token being bridged
                              address wrapperTokenAddress;
                              /// @notice socket offchain created hash
                              bytes32 metadata;
                          }
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct AnyswapBridgeData {
                              /// @notice destination ChainId
                              uint256 toChainId;
                              /// @notice address of receiver of bridged tokens
                              address receiverAddress;
                              /// @notice address of wrapperToken, WrappedVersion of the token being bridged
                              address wrapperTokenAddress;
                              /// @notice address of token being bridged
                              address token;
                              /// @notice socket offchain created hash
                              bytes32 metadata;
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct
                           * @param amount amount of tokens being bridged. this can be ERC20 or native
                           * @param bridgeData encoded data for AnyswapBridge
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable override {
                              AnyswapBridgeData memory anyswapBridgeData = abi.decode(
                                  bridgeData,
                                  (AnyswapBridgeData)
                              );
                              ERC20(anyswapBridgeData.token).safeApprove(address(router), amount);
                              router.anySwapOutUnderlying(
                                  anyswapBridgeData.wrapperTokenAddress,
                                  anyswapBridgeData.receiverAddress,
                                  amount,
                                  anyswapBridgeData.toChainId
                              );
                              emit SocketBridge(
                                  amount,
                                  anyswapBridgeData.token,
                                  anyswapBridgeData.toChainId,
                                  AnyswapIdentifier,
                                  msg.sender,
                                  anyswapBridgeData.receiverAddress,
                                  anyswapBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct
                           * @param swapId routeId for the swapImpl
                           * @param swapData encoded data for swap
                           * @param anyswapBridgeData encoded data for AnyswapBridge
                           */
                          function swapAndBridge(
                              uint32 swapId,
                              bytes calldata swapData,
                              AnyswapBridgeDataNoToken calldata anyswapBridgeData
                          ) external payable {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(swapId)
                                  .delegatecall(swapData);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              (uint256 bridgeAmount, address token) = abi.decode(
                                  result,
                                  (uint256, address)
                              );
                              ERC20(token).safeApprove(address(router), bridgeAmount);
                              router.anySwapOutUnderlying(
                                  anyswapBridgeData.wrapperTokenAddress,
                                  anyswapBridgeData.receiverAddress,
                                  bridgeAmount,
                                  anyswapBridgeData.toChainId
                              );
                              emit SocketBridge(
                                  bridgeAmount,
                                  token,
                                  anyswapBridgeData.toChainId,
                                  AnyswapIdentifier,
                                  msg.sender,
                                  anyswapBridgeData.receiverAddress,
                                  anyswapBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to handle ERC20 bridging to receipent via Anyswap-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param amount amount being bridged
                           * @param toChainId destination ChainId
                           * @param receiverAddress address of receiver of bridged tokens
                           * @param token address of token being bridged
                           * @param wrapperTokenAddress address of wrapperToken, WrappedVersion of the token being bridged
                           */
                          function bridgeERC20To(
                              uint256 amount,
                              uint256 toChainId,
                              bytes32 metadata,
                              address receiverAddress,
                              address token,
                              address wrapperTokenAddress
                          ) external payable {
                              ERC20 tokenInstance = ERC20(token);
                              tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);
                              tokenInstance.safeApprove(address(router), amount);
                              router.anySwapOutUnderlying(
                                  wrapperTokenAddress,
                                  receiverAddress,
                                  amount,
                                  toChainId
                              );
                              emit SocketBridge(
                                  amount,
                                  token,
                                  toChainId,
                                  AnyswapIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                      }
                      // SPDX-License-Identifier: Apache-2.0
                      /*
                       * Copyright 2021, Offchain Labs, Inc.
                       *
                       * Licensed under the Apache License, Version 2.0 (the "License");
                       * you may not use this file except in compliance with the License.
                       * You may obtain a copy of the License at
                       *
                       *    http://www.apache.org/licenses/LICENSE-2.0
                       *
                       * Unless required by applicable law or agreed to in writing, software
                       * distributed under the License is distributed on an "AS IS" BASIS,
                       * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                       * See the License for the specific language governing permissions and
                       * limitations under the License.
                       */
                      pragma solidity >=0.8.0;
                      /**
                       * @title L1gatewayRouter for native-arbitrum
                       */
                      interface L1GatewayRouter {
                          /**
                           * @notice outbound function to bridge ERC20 via NativeArbitrum-Bridge
                           * @param _token address of token being bridged via GatewayRouter
                           * @param _to recipient of the token on arbitrum chain
                           * @param _amount amount of ERC20 token being bridged
                           * @param _maxGas a depositParameter for bridging the token
                           * @param _gasPriceBid  a depositParameter for bridging the token
                           * @param _data a depositParameter for bridging the token
                           * @return calldata returns the output of transactioncall made on gatewayRouter
                           */
                          function outboundTransfer(
                              address _token,
                              address _to,
                              uint256 _amount,
                              uint256 _maxGas,
                              uint256 _gasPriceBid,
                              bytes calldata _data
                          ) external payable returns (bytes calldata);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.0;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {L1GatewayRouter} from "../interfaces/arbitrum.sol";
                      import {BridgeImplBase} from "../../BridgeImplBase.sol";
                      import {NATIVE_ARBITRUM} from "../../../static/RouteIdentifiers.sol";
                      /**
                       * @title Native Arbitrum-Route Implementation
                       * @notice Route implementation with functions to bridge ERC20 via NativeArbitrum-Bridge
                       * @notice Called via SocketGateway if the routeId in the request maps to the routeId of NativeArbitrum-Implementation
                       * @notice This is used when transferring from ethereum chain to arbitrum via their native bridge.
                       * @notice Contains function to handle bridging as post-step i.e linked to a preceeding step for swap
                       * @notice RequestData is different to just bride and bridging chained with swap
                       * @author Socket dot tech.
                       */
                      contract NativeArbitrumImpl is BridgeImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable NativeArbitrumIdentifier = NATIVE_ARBITRUM;
                          uint256 public constant DESTINATION_CHAIN_ID = 42161;
                          /// @notice Function-selector for ERC20-token bridging on NativeArbitrum
                          /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens
                          bytes4
                              public immutable NATIVE_ARBITRUM_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeERC20To(uint256,uint256,uint256,uint256,bytes32,address,address,address,bytes)"
                                  )
                              );
                          bytes4 public immutable NATIVE_ARBITRUM_SWAP_BRIDGE_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "swapAndBridge(uint32,bytes,(uint256,uint256,uint256,address,address,bytes32,bytes))"
                                  )
                              );
                          /// @notice router address of NativeArbitrum Bridge
                          /// @notice GatewayRouter looks up ERC20Token's gateway, and finding that it's Standard ERC20 gateway (the L1ERC20Gateway contract).
                          address public immutable router;
                          /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase
                          /// @dev ensure router-address are set properly for the chainId in which the contract is being deployed
                          constructor(
                              address _router,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) BridgeImplBase(_socketGateway, _socketDeployFactory) {
                              router = _router;
                          }
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct NativeArbitrumBridgeDataNoToken {
                              uint256 value;
                              /// @notice maxGas is a depositParameter derived from erc20Bridger of nativeArbitrum
                              uint256 maxGas;
                              /// @notice gasPriceBid is a depositParameter derived from erc20Bridger of nativeArbitrum
                              uint256 gasPriceBid;
                              /// @notice address of receiver of bridged tokens
                              address receiverAddress;
                              /// @notice address of Gateway which handles the token bridging for the token
                              /// @notice gatewayAddress is unique for each token
                              address gatewayAddress;
                              /// @notice socket offchain created hash
                              bytes32 metadata;
                              /// @notice data is a depositParameter derived from erc20Bridger of nativeArbitrum
                              bytes data;
                          }
                          struct NativeArbitrumBridgeData {
                              uint256 value;
                              /// @notice maxGas is a depositParameter derived from erc20Bridger of nativeArbitrum
                              uint256 maxGas;
                              /// @notice gasPriceBid is a depositParameter derived from erc20Bridger of nativeArbitrum
                              uint256 gasPriceBid;
                              /// @notice address of receiver of bridged tokens
                              address receiverAddress;
                              /// @notice address of Gateway which handles the token bridging for the token
                              /// @notice gatewayAddress is unique for each token
                              address gatewayAddress;
                              /// @notice address of token being bridged
                              address token;
                              /// @notice socket offchain created hash
                              bytes32 metadata;
                              /// @notice data is a depositParameter derived from erc20Bridger of nativeArbitrum
                              bytes data;
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in NativeArbitrumBridgeData struct
                           * @param amount amount of tokens being bridged. this can be ERC20 or native
                           * @param bridgeData encoded data for NativeArbitrumBridge
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable override {
                              NativeArbitrumBridgeData memory nativeArbitrumBridgeData = abi.decode(
                                  bridgeData,
                                  (NativeArbitrumBridgeData)
                              );
                              ERC20(nativeArbitrumBridgeData.token).safeApprove(
                                  nativeArbitrumBridgeData.gatewayAddress,
                                  amount
                              );
                              L1GatewayRouter(router).outboundTransfer{
                                  value: nativeArbitrumBridgeData.value
                              }(
                                  nativeArbitrumBridgeData.token,
                                  nativeArbitrumBridgeData.receiverAddress,
                                  amount,
                                  nativeArbitrumBridgeData.maxGas,
                                  nativeArbitrumBridgeData.gasPriceBid,
                                  nativeArbitrumBridgeData.data
                              );
                              emit SocketBridge(
                                  amount,
                                  nativeArbitrumBridgeData.token,
                                  DESTINATION_CHAIN_ID,
                                  NativeArbitrumIdentifier,
                                  msg.sender,
                                  nativeArbitrumBridgeData.receiverAddress,
                                  nativeArbitrumBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in NativeArbitrumBridgeData struct
                           * @param swapId routeId for the swapImpl
                           * @param swapData encoded data for swap
                           * @param nativeArbitrumBridgeData encoded data for NativeArbitrumBridge
                           */
                          function swapAndBridge(
                              uint32 swapId,
                              bytes calldata swapData,
                              NativeArbitrumBridgeDataNoToken calldata nativeArbitrumBridgeData
                          ) external payable {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(swapId)
                                  .delegatecall(swapData);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              (uint256 bridgeAmount, address token) = abi.decode(
                                  result,
                                  (uint256, address)
                              );
                              ERC20(token).safeApprove(
                                  nativeArbitrumBridgeData.gatewayAddress,
                                  bridgeAmount
                              );
                              L1GatewayRouter(router).outboundTransfer{
                                  value: nativeArbitrumBridgeData.value
                              }(
                                  token,
                                  nativeArbitrumBridgeData.receiverAddress,
                                  bridgeAmount,
                                  nativeArbitrumBridgeData.maxGas,
                                  nativeArbitrumBridgeData.gasPriceBid,
                                  nativeArbitrumBridgeData.data
                              );
                              emit SocketBridge(
                                  bridgeAmount,
                                  token,
                                  DESTINATION_CHAIN_ID,
                                  NativeArbitrumIdentifier,
                                  msg.sender,
                                  nativeArbitrumBridgeData.receiverAddress,
                                  nativeArbitrumBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to handle ERC20 bridging to receipent via NativeArbitrum-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param amount amount being bridged
                           * @param value value
                           * @param maxGas maxGas is a depositParameter derived from erc20Bridger of nativeArbitrum
                           * @param gasPriceBid gasPriceBid is a depositParameter derived from erc20Bridger of nativeArbitrum
                           * @param receiverAddress address of receiver of bridged tokens
                           * @param token address of token being bridged
                           * @param gatewayAddress address of Gateway which handles the token bridging for the token, gatewayAddress is unique for each token
                           * @param data data is a depositParameter derived from erc20Bridger of nativeArbitrum
                           */
                          function bridgeERC20To(
                              uint256 amount,
                              uint256 value,
                              uint256 maxGas,
                              uint256 gasPriceBid,
                              bytes32 metadata,
                              address receiverAddress,
                              address token,
                              address gatewayAddress,
                              bytes memory data
                          ) external payable {
                              ERC20 tokenInstance = ERC20(token);
                              tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);
                              tokenInstance.safeApprove(gatewayAddress, amount);
                              L1GatewayRouter(router).outboundTransfer{value: value}(
                                  token,
                                  receiverAddress,
                                  amount,
                                  maxGas,
                                  gasPriceBid,
                                  data
                              );
                              emit SocketBridge(
                                  amount,
                                  token,
                                  DESTINATION_CHAIN_ID,
                                  NativeArbitrumIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {ISocketGateway} from "../interfaces/ISocketGateway.sol";
                      import {ISocketRoute} from "../interfaces/ISocketRoute.sol";
                      import {OnlySocketGatewayOwner, OnlySocketDeployer} from "../errors/SocketErrors.sol";
                      /**
                       * @title Abstract Implementation Contract.
                       * @notice All Bridge Implementation will follow this interface.
                       */
                      abstract contract BridgeImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          /// @notice Address used to identify if it is a native token transfer or not
                          address public immutable NATIVE_TOKEN_ADDRESS =
                              address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
                          /// @notice immutable variable to store the socketGateway address
                          address public immutable socketGateway;
                          /// @notice immutable variable to store the socketGateway address
                          address public immutable socketDeployFactory;
                          /// @notice immutable variable with instance of SocketRoute to access route functions
                          ISocketRoute public immutable socketRoute;
                          /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation
                          bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =
                              bytes4(keccak256("bridgeAfterSwap(uint256,bytes)"));
                          /****************************************
                           *               EVENTS                 *
                           ****************************************/
                          event SocketBridge(
                              uint256 amount,
                              address token,
                              uint256 toChainId,
                              bytes32 bridgeName,
                              address sender,
                              address receiver,
                              bytes32 metadata
                          );
                          /**
                           * @notice Construct the base for all BridgeImplementations.
                           * @param _socketGateway Socketgateway address, an immutable variable to set.
                           * @param _socketDeployFactory Socket Deploy Factory address, an immutable variable to set.
                           */
                          constructor(address _socketGateway, address _socketDeployFactory) {
                              socketGateway = _socketGateway;
                              socketDeployFactory = _socketDeployFactory;
                              socketRoute = ISocketRoute(_socketGateway);
                          }
                          /****************************************
                           *               MODIFIERS              *
                           ****************************************/
                          /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used
                          modifier isSocketGatewayOwner() {
                              if (msg.sender != ISocketGateway(socketGateway).owner()) {
                                  revert OnlySocketGatewayOwner();
                              }
                              _;
                          }
                          /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used
                          modifier isSocketDeployFactory() {
                              if (msg.sender != socketDeployFactory) {
                                  revert OnlySocketDeployer();
                              }
                              _;
                          }
                          /****************************************
                           *    RESTRICTED FUNCTIONS              *
                           ****************************************/
                          /**
                           * @notice function to rescue the ERC20 tokens in the bridge Implementation contract
                           * @notice this is a function restricted to Owner of SocketGateway only
                           * @param token address of ERC20 token being rescued
                           * @param userAddress receipient address to which ERC20 tokens will be rescued to
                           * @param amount amount of ERC20 tokens being rescued
                           */
                          function rescueFunds(
                              address token,
                              address userAddress,
                              uint256 amount
                          ) external isSocketGatewayOwner {
                              ERC20(token).safeTransfer(userAddress, amount);
                          }
                          /**
                           * @notice function to rescue the native-balance in the bridge Implementation contract
                           * @notice this is a function restricted to Owner of SocketGateway only
                           * @param userAddress receipient address to which native-balance will be rescued to
                           * @param amount amount of native balance tokens being rescued
                           */
                          function rescueEther(
                              address payable userAddress,
                              uint256 amount
                          ) external isSocketGatewayOwner {
                              userAddress.transfer(amount);
                          }
                          function killme() external isSocketDeployFactory {
                              selfdestruct(payable(msg.sender));
                          }
                          /******************************
                           *    VIRTUAL FUNCTIONS       *
                           *****************************/
                          /**
                           * @notice function to bridge which is succeeding the swap function
                           * @notice this function is to be used only when bridging as a succeeding step
                           * @notice All bridge implementation contracts must implement this function
                           * @notice bridge-implementations will have a bridge specific struct with properties used in bridging
                           * @param bridgeData encoded value of properties in the bridgeData Struct
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable virtual;
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import "../../libraries/Pb.sol";
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import "./interfaces/cbridge.sol";
                      import "./interfaces/ICelerStorageWrapper.sol";
                      import {TransferIdExists, InvalidCelerRefund, CelerAlreadyRefunded, CelerRefundNotReady} from "../../errors/SocketErrors.sol";
                      import {BridgeImplBase} from "../BridgeImplBase.sol";
                      import {CBRIDGE} from "../../static/RouteIdentifiers.sol";
                      /**
                       * @title Celer-Route Implementation
                       * @notice Route implementation with functions to bridge ERC20 and Native via Celer-Bridge
                       * Called via SocketGateway if the routeId in the request maps to the routeId of CelerImplementation
                       * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap
                       * RequestData is different to just bride and bridging chained with swap
                       * @author Socket dot tech.
                       */
                      contract CelerImpl is BridgeImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable CBridgeIdentifier = CBRIDGE;
                          /// @notice Utility to perform operation on Buffer
                          using Pb for Pb.Buffer;
                          /// @notice Function-selector for ERC20-token bridging on Celer-Route
                          /// @dev This function selector is to be used while building transaction-data to bridge ERC20 tokens
                          bytes4 public immutable CELER_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeERC20To(address,address,uint256,bytes32,uint64,uint64,uint32)"
                                  )
                              );
                          /// @notice Function-selector for Native bridging on Celer-Route
                          /// @dev This function selector is to be used while building transaction-data to bridge Native tokens
                          bytes4 public immutable CELER_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeNativeTo(address,uint256,bytes32,uint64,uint64,uint32)"
                                  )
                              );
                          bytes4 public immutable CELER_SWAP_BRIDGE_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "swapAndBridge(uint32,bytes,(address,uint64,uint32,uint64,bytes32))"
                                  )
                              );
                          /// @notice router Contract instance used to deposit ERC20 and Native on to Celer-Bridge
                          /// @dev contract instance is to be initialized in the constructor using the routerAddress passed as constructor argument
                          ICBridge public immutable router;
                          /// @notice celerStorageWrapper Contract instance used to store the transferId generated during ERC20 and Native bridge on to Celer-Bridge
                          /// @dev contract instance is to be initialized in the constructor using the celerStorageWrapperAddress passed as constructor argument
                          ICelerStorageWrapper public immutable celerStorageWrapper;
                          /// @notice WETH token address
                          address public immutable weth;
                          /// @notice chainId used during generation of transferId generated while bridging ERC20 and Native on to Celer-Bridge
                          /// @dev this is to be initialised in the constructor
                          uint64 public immutable chainId;
                          struct WithdrawMsg {
                              uint64 chainid; // tag: 1
                              uint64 seqnum; // tag: 2
                              address receiver; // tag: 3
                              address token; // tag: 4
                              uint256 amount; // tag: 5
                              bytes32 refid; // tag: 6
                          }
                          /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase
                          /// @dev ensure routerAddress, weth-address, celerStorageWrapperAddress are set properly for the chainId in which the contract is being deployed
                          constructor(
                              address _routerAddress,
                              address _weth,
                              address _celerStorageWrapperAddress,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) BridgeImplBase(_socketGateway, _socketDeployFactory) {
                              router = ICBridge(_routerAddress);
                              celerStorageWrapper = ICelerStorageWrapper(_celerStorageWrapperAddress);
                              weth = _weth;
                              chainId = uint64(block.chainid);
                          }
                          // Function to receive Ether. msg.data must be empty
                          receive() external payable {}
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct CelerBridgeDataNoToken {
                              address receiverAddress;
                              uint64 toChainId;
                              uint32 maxSlippage;
                              uint64 nonce;
                              bytes32 metadata;
                          }
                          struct CelerBridgeData {
                              address token;
                              address receiverAddress;
                              uint64 toChainId;
                              uint32 maxSlippage;
                              uint64 nonce;
                              bytes32 metadata;
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in CelerBridgeData struct
                           * @param amount amount of tokens being bridged. this can be ERC20 or native
                           * @param bridgeData encoded data for CelerBridge
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable override {
                              CelerBridgeData memory celerBridgeData = abi.decode(
                                  bridgeData,
                                  (CelerBridgeData)
                              );
                              if (celerBridgeData.token == NATIVE_TOKEN_ADDRESS) {
                                  // transferId is generated using the request-params and nonce of the account
                                  // transferId should be unique for each request and this is used while handling refund from celerBridge
                                  bytes32 transferId = keccak256(
                                      abi.encodePacked(
                                          address(this),
                                          celerBridgeData.receiverAddress,
                                          weth,
                                          amount,
                                          celerBridgeData.toChainId,
                                          celerBridgeData.nonce,
                                          chainId
                                      )
                                  );
                                  // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender
                                  celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);
                                  router.sendNative{value: amount}(
                                      celerBridgeData.receiverAddress,
                                      amount,
                                      celerBridgeData.toChainId,
                                      celerBridgeData.nonce,
                                      celerBridgeData.maxSlippage
                                  );
                              } else {
                                  // transferId is generated using the request-params and nonce of the account
                                  // transferId should be unique for each request and this is used while handling refund from celerBridge
                                  bytes32 transferId = keccak256(
                                      abi.encodePacked(
                                          address(this),
                                          celerBridgeData.receiverAddress,
                                          celerBridgeData.token,
                                          amount,
                                          celerBridgeData.toChainId,
                                          celerBridgeData.nonce,
                                          chainId
                                      )
                                  );
                                  // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender
                                  celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);
                                  router.send(
                                      celerBridgeData.receiverAddress,
                                      celerBridgeData.token,
                                      amount,
                                      celerBridgeData.toChainId,
                                      celerBridgeData.nonce,
                                      celerBridgeData.maxSlippage
                                  );
                              }
                              emit SocketBridge(
                                  amount,
                                  celerBridgeData.token,
                                  celerBridgeData.toChainId,
                                  CBridgeIdentifier,
                                  msg.sender,
                                  celerBridgeData.receiverAddress,
                                  celerBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in CelerBridgeData struct
                           * @param swapId routeId for the swapImpl
                           * @param swapData encoded data for swap
                           * @param celerBridgeData encoded data for CelerBridgeData
                           */
                          function swapAndBridge(
                              uint32 swapId,
                              bytes calldata swapData,
                              CelerBridgeDataNoToken calldata celerBridgeData
                          ) external payable {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(swapId)
                                  .delegatecall(swapData);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              (uint256 bridgeAmount, address token) = abi.decode(
                                  result,
                                  (uint256, address)
                              );
                              if (token == NATIVE_TOKEN_ADDRESS) {
                                  // transferId is generated using the request-params and nonce of the account
                                  // transferId should be unique for each request and this is used while handling refund from celerBridge
                                  bytes32 transferId = keccak256(
                                      abi.encodePacked(
                                          address(this),
                                          celerBridgeData.receiverAddress,
                                          weth,
                                          bridgeAmount,
                                          celerBridgeData.toChainId,
                                          celerBridgeData.nonce,
                                          chainId
                                      )
                                  );
                                  // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender
                                  celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);
                                  router.sendNative{value: bridgeAmount}(
                                      celerBridgeData.receiverAddress,
                                      bridgeAmount,
                                      celerBridgeData.toChainId,
                                      celerBridgeData.nonce,
                                      celerBridgeData.maxSlippage
                                  );
                              } else {
                                  // transferId is generated using the request-params and nonce of the account
                                  // transferId should be unique for each request and this is used while handling refund from celerBridge
                                  bytes32 transferId = keccak256(
                                      abi.encodePacked(
                                          address(this),
                                          celerBridgeData.receiverAddress,
                                          token,
                                          bridgeAmount,
                                          celerBridgeData.toChainId,
                                          celerBridgeData.nonce,
                                          chainId
                                      )
                                  );
                                  // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender
                                  celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);
                                  router.send(
                                      celerBridgeData.receiverAddress,
                                      token,
                                      bridgeAmount,
                                      celerBridgeData.toChainId,
                                      celerBridgeData.nonce,
                                      celerBridgeData.maxSlippage
                                  );
                              }
                              emit SocketBridge(
                                  bridgeAmount,
                                  token,
                                  celerBridgeData.toChainId,
                                  CBridgeIdentifier,
                                  msg.sender,
                                  celerBridgeData.receiverAddress,
                                  celerBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to handle ERC20 bridging to receipent via Celer-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param receiverAddress address of recipient
                           * @param token address of token being bridged
                           * @param amount amount of token for bridging
                           * @param toChainId destination ChainId
                           * @param nonce nonce of the sender-account address
                           * @param maxSlippage maximum Slippage for the bridging
                           */
                          function bridgeERC20To(
                              address receiverAddress,
                              address token,
                              uint256 amount,
                              bytes32 metadata,
                              uint64 toChainId,
                              uint64 nonce,
                              uint32 maxSlippage
                          ) external payable {
                              /// @notice transferId is generated using the request-params and nonce of the account
                              /// @notice transferId should be unique for each request and this is used while handling refund from celerBridge
                              bytes32 transferId = keccak256(
                                  abi.encodePacked(
                                      address(this),
                                      receiverAddress,
                                      token,
                                      amount,
                                      toChainId,
                                      nonce,
                                      chainId
                                  )
                              );
                              /// @notice stored in the CelerStorageWrapper contract
                              celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);
                              ERC20 tokenInstance = ERC20(token);
                              tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);
                              router.send(
                                  receiverAddress,
                                  token,
                                  amount,
                                  toChainId,
                                  nonce,
                                  maxSlippage
                              );
                              emit SocketBridge(
                                  amount,
                                  token,
                                  toChainId,
                                  CBridgeIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                          /**
                           * @notice function to handle Native bridging to receipent via Celer-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param receiverAddress address of recipient
                           * @param amount amount of token for bridging
                           * @param toChainId destination ChainId
                           * @param nonce nonce of the sender-account address
                           * @param maxSlippage maximum Slippage for the bridging
                           */
                          function bridgeNativeTo(
                              address receiverAddress,
                              uint256 amount,
                              bytes32 metadata,
                              uint64 toChainId,
                              uint64 nonce,
                              uint32 maxSlippage
                          ) external payable {
                              bytes32 transferId = keccak256(
                                  abi.encodePacked(
                                      address(this),
                                      receiverAddress,
                                      weth,
                                      amount,
                                      toChainId,
                                      nonce,
                                      chainId
                                  )
                              );
                              celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);
                              router.sendNative{value: amount}(
                                  receiverAddress,
                                  amount,
                                  toChainId,
                                  nonce,
                                  maxSlippage
                              );
                              emit SocketBridge(
                                  amount,
                                  NATIVE_TOKEN_ADDRESS,
                                  toChainId,
                                  CBridgeIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                          /**
                           * @notice function to handle refund from CelerBridge-Router
                           * @param _request request data generated offchain using the celer-SDK
                           * @param _sigs generated offchain using the celer-SDK
                           * @param _signers  generated offchain using the celer-SDK
                           * @param _powers generated offchain using the celer-SDK
                           */
                          function refundCelerUser(
                              bytes calldata _request,
                              bytes[] calldata _sigs,
                              address[] calldata _signers,
                              uint256[] calldata _powers
                          ) external payable {
                              WithdrawMsg memory request = decWithdrawMsg(_request);
                              bytes32 transferId = keccak256(
                                  abi.encodePacked(
                                      request.chainid,
                                      request.seqnum,
                                      request.receiver,
                                      request.token,
                                      request.amount
                                  )
                              );
                              uint256 _initialNativeBalance = address(this).balance;
                              uint256 _initialTokenBalance = ERC20(request.token).balanceOf(
                                  address(this)
                              );
                              if (!router.withdraws(transferId)) {
                                  router.withdraw(_request, _sigs, _signers, _powers);
                              }
                              if (request.receiver != socketGateway) {
                                  revert InvalidCelerRefund();
                              }
                              address _receiver = celerStorageWrapper.getAddressFromTransferId(
                                  request.refid
                              );
                              celerStorageWrapper.deleteTransferId(request.refid);
                              if (_receiver == address(0)) {
                                  revert CelerAlreadyRefunded();
                              }
                              uint256 _nativeBalanceAfter = address(this).balance;
                              uint256 _tokenBalanceAfter = ERC20(request.token).balanceOf(
                                  address(this)
                              );
                              if (_nativeBalanceAfter > _initialNativeBalance) {
                                  if ((_nativeBalanceAfter - _initialNativeBalance) != request.amount)
                                      revert CelerRefundNotReady();
                                  payable(_receiver).transfer(request.amount);
                                  return;
                              }
                              if (_tokenBalanceAfter > _initialTokenBalance) {
                                  if ((_tokenBalanceAfter - _initialTokenBalance) != request.amount)
                                      revert CelerRefundNotReady();
                                  ERC20(request.token).safeTransfer(_receiver, request.amount);
                                  return;
                              }
                              revert CelerRefundNotReady();
                          }
                          function decWithdrawMsg(
                              bytes memory raw
                          ) internal pure returns (WithdrawMsg memory m) {
                              Pb.Buffer memory buf = Pb.fromBytes(raw);
                              uint256 tag;
                              Pb.WireType wire;
                              while (buf.hasMore()) {
                                  (tag, wire) = buf.decKey();
                                  if (false) {}
                                  // solidity has no switch/case
                                  else if (tag == 1) {
                                      m.chainid = uint64(buf.decVarint());
                                  } else if (tag == 2) {
                                      m.seqnum = uint64(buf.decVarint());
                                  } else if (tag == 3) {
                                      m.receiver = Pb._address(buf.decBytes());
                                  } else if (tag == 4) {
                                      m.token = Pb._address(buf.decBytes());
                                  } else if (tag == 5) {
                                      m.amount = Pb._uint256(buf.decBytes());
                                  } else if (tag == 6) {
                                      m.refid = Pb._bytes32(buf.decBytes());
                                  } else {
                                      buf.skipValue(wire);
                                  } // skip value of unknown tag
                              }
                          } // end decoder WithdrawMsg
                      }
                      // SPDX-License-Identifier: Apache-2.0
                      pragma solidity >=0.8.0;
                      import {OnlySocketGateway, TransferIdExists, TransferIdDoesnotExist} from "../../errors/SocketErrors.sol";
                      /**
                       * @title CelerStorageWrapper
                       * @notice handle storageMappings used while bridging ERC20 and native on CelerBridge
                       * @dev all functions ehich mutate the storage are restricted to Owner of SocketGateway
                       * @author Socket dot tech.
                       */
                      contract CelerStorageWrapper {
                          /// @notice Socketgateway-address to be set in the constructor of CelerStorageWrapper
                          address public immutable socketGateway;
                          /// @notice mapping to store the transferId generated during bridging on Celer to message-sender
                          mapping(bytes32 => address) private transferIdMapping;
                          /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase
                          constructor(address _socketGateway) {
                              socketGateway = _socketGateway;
                          }
                          /**
                           * @notice function to store the transferId and message-sender of a bridging activity
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in CelerBridgeData struct
                           * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge
                           * @param transferIdAddress message sender who is making the bridging on CelerBridge
                           */
                          function setAddressForTransferId(
                              bytes32 transferId,
                              address transferIdAddress
                          ) external {
                              if (msg.sender != socketGateway) {
                                  revert OnlySocketGateway();
                              }
                              if (transferIdMapping[transferId] != address(0)) {
                                  revert TransferIdExists();
                              }
                              transferIdMapping[transferId] = transferIdAddress;
                          }
                          /**
                           * @notice function to delete the transferId when the celer bridge processes a refund.
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in CelerBridgeData struct
                           * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge
                           */
                          function deleteTransferId(bytes32 transferId) external {
                              if (msg.sender != socketGateway) {
                                  revert OnlySocketGateway();
                              }
                              if (transferIdMapping[transferId] == address(0)) {
                                  revert TransferIdDoesnotExist();
                              }
                              delete transferIdMapping[transferId];
                          }
                          /**
                           * @notice function to lookup the address mapped to the transferId
                           * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge
                           * @return address of account mapped to transferId
                           */
                          function getAddressFromTransferId(
                              bytes32 transferId
                          ) external view returns (address) {
                              return transferIdMapping[transferId];
                          }
                      }
                      // SPDX-License-Identifier: Apache-2.0
                      pragma solidity >=0.8.0;
                      interface ICBridge {
                          function send(
                              address _receiver,
                              address _token,
                              uint256 _amount,
                              uint64 _dstChinId,
                              uint64 _nonce,
                              uint32 _maxSlippage
                          ) external;
                          function sendNative(
                              address _receiver,
                              uint256 _amount,
                              uint64 _dstChinId,
                              uint64 _nonce,
                              uint32 _maxSlippage
                          ) external payable;
                          function withdraws(bytes32 withdrawId) external view returns (bool);
                          function withdraw(
                              bytes calldata _wdmsg,
                              bytes[] calldata _sigs,
                              address[] calldata _signers,
                              uint256[] calldata _powers
                          ) external;
                      }
                      // SPDX-License-Identifier: Apache-2.0
                      pragma solidity >=0.8.0;
                      /**
                       * @title Celer-StorageWrapper interface
                       * @notice Interface to handle storageMappings used while bridging ERC20 and native on CelerBridge
                       * @dev all functions ehich mutate the storage are restricted to Owner of SocketGateway
                       * @author Socket dot tech.
                       */
                      interface ICelerStorageWrapper {
                          /**
                           * @notice function to store the transferId and message-sender of a bridging activity
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in CelerBridgeData struct
                           * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge
                           * @param transferIdAddress message sender who is making the bridging on CelerBridge
                           */
                          function setAddressForTransferId(
                              bytes32 transferId,
                              address transferIdAddress
                          ) external;
                          /**
                           * @notice function to store the transferId and message-sender of a bridging activity
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in CelerBridgeData struct
                           * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge
                           */
                          function deleteTransferId(bytes32 transferId) external;
                          /**
                           * @notice function to lookup the address mapped to the transferId
                           * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge
                           * @return address of account mapped to transferId
                           */
                          function getAddressFromTransferId(
                              bytes32 transferId
                          ) external view returns (address);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.0;
                      /**
                       * @title HopAMM
                       * @notice Interface to handle the token bridging to L2 chains.
                       */
                      interface HopAMM {
                          /**
                           * @notice To send funds L2->L1 or L2->L2, call the swapAndSend on the L2 AMM Wrapper contract
                           * @param chainId chainId of the L2 contract
                           * @param recipient receiver address
                           * @param amount amount is the amount the user wants to send plus the Bonder fee
                           * @param bonderFee fees
                           * @param amountOutMin minimum amount
                           * @param deadline deadline for bridging
                           * @param destinationAmountOutMin minimum amount expected to be bridged on L2
                           * @param destinationDeadline destination time before which token is to be bridged on L2
                           */
                          function swapAndSend(
                              uint256 chainId,
                              address recipient,
                              uint256 amount,
                              uint256 bonderFee,
                              uint256 amountOutMin,
                              uint256 deadline,
                              uint256 destinationAmountOutMin,
                              uint256 destinationDeadline
                          ) external payable;
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      /**
                       * @title L1Bridge Hop Interface
                       * @notice L1 Hop Bridge, Used to transfer from L1 to L2s.
                       */
                      interface IHopL1Bridge {
                          /**
                           * @notice `amountOutMin` and `deadline` should be 0 when no swap is intended at the destination.
                           * @notice `amount` is the total amount the user wants to send including the relayer fee
                           * @dev Send tokens to a supported layer-2 to mint hToken and optionally swap the hToken in the
                           * AMM at the destination.
                           * @param chainId The chainId of the destination chain
                           * @param recipient The address receiving funds at the destination
                           * @param amount The amount being sent
                           * @param amountOutMin The minimum amount received after attempting to swap in the destination
                           * AMM market. 0 if no swap is intended.
                           * @param deadline The deadline for swapping in the destination AMM market. 0 if no
                           * swap is intended.
                           * @param relayer The address of the relayer at the destination.
                           * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`.
                           */
                          function sendToL2(
                              uint256 chainId,
                              address recipient,
                              uint256 amount,
                              uint256 amountOutMin,
                              uint256 deadline,
                              address relayer,
                              uint256 relayerFee
                          ) external payable;
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.0;
                      import "../interfaces/IHopL1Bridge.sol";
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {BridgeImplBase} from "../../BridgeImplBase.sol";
                      import {HOP} from "../../../static/RouteIdentifiers.sol";
                      /**
                       * @title Hop-L1 Route Implementation
                       * @notice Route implementation with functions to bridge ERC20 and Native via Hop-Bridge from L1 to Supported L2s
                       * Called via SocketGateway if the routeId in the request maps to the routeId of HopImplementation
                       * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap
                       * RequestData is different to just bride and bridging chained with swap
                       * @author Socket dot tech.
                       */
                      contract HopImplL1 is BridgeImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable HopIdentifier = HOP;
                          /// @notice Function-selector for ERC20-token bridging on Hop-L1-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens
                          bytes4 public immutable HOP_L1_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeERC20To(address,address,address,address,uint256,uint256,uint256,uint256,(uint256,bytes32))"
                                  )
                              );
                          /// @notice Function-selector for Native bridging on Hop-L1-Route
                          /// @dev This function selector is to be used while building transaction-data to bridge Native tokens
                          bytes4 public immutable HOP_L1_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeNativeTo(address,address,address,uint256,uint256,uint256,uint256,uint256,bytes32)"
                                  )
                              );
                          bytes4 public immutable HOP_L1_SWAP_BRIDGE_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "swapAndBridge(uint32,bytes,(address,address,address,uint256,uint256,uint256,uint256,bytes32))"
                                  )
                              );
                          /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase
                          constructor(
                              address _socketGateway,
                              address _socketDeployFactory
                          ) BridgeImplBase(_socketGateway, _socketDeployFactory) {}
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct HopDataNoToken {
                              // The address receiving funds at the destination
                              address receiverAddress;
                              // address of the Hop-L1-Bridge to handle bridging the tokens
                              address l1bridgeAddr;
                              // relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.
                              address relayer;
                              // The chainId of the destination chain
                              uint256 toChainId;
                              // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.
                              uint256 amountOutMin;
                              // The amount distributed to the relayer at the destination. This is subtracted from the `amount`.
                              uint256 relayerFee;
                              // The deadline for swapping in the destination AMM market. 0 if no swap is intended.
                              uint256 deadline;
                              // socket offchain created hash
                              bytes32 metadata;
                          }
                          struct HopData {
                              /// @notice address of token being bridged
                              address token;
                              // The address receiving funds at the destination
                              address receiverAddress;
                              // address of the Hop-L1-Bridge to handle bridging the tokens
                              address l1bridgeAddr;
                              // relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.
                              address relayer;
                              // The chainId of the destination chain
                              uint256 toChainId;
                              // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.
                              uint256 amountOutMin;
                              // The amount distributed to the relayer at the destination. This is subtracted from the `amount`.
                              uint256 relayerFee;
                              // The deadline for swapping in the destination AMM market. 0 if no swap is intended.
                              uint256 deadline;
                              // socket offchain created hash
                              bytes32 metadata;
                          }
                          struct HopERC20Data {
                              uint256 deadline;
                              bytes32 metadata;
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in HopBridgeData struct
                           * @param amount amount of tokens being bridged. this can be ERC20 or native
                           * @param bridgeData encoded data for Hop-L1-Bridge
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable override {
                              HopData memory hopData = abi.decode(bridgeData, (HopData));
                              if (hopData.token == NATIVE_TOKEN_ADDRESS) {
                                  IHopL1Bridge(hopData.l1bridgeAddr).sendToL2{value: amount}(
                                      hopData.toChainId,
                                      hopData.receiverAddress,
                                      amount,
                                      hopData.amountOutMin,
                                      hopData.deadline,
                                      hopData.relayer,
                                      hopData.relayerFee
                                  );
                              } else {
                                  ERC20(hopData.token).safeApprove(hopData.l1bridgeAddr, amount);
                                  // perform bridging
                                  IHopL1Bridge(hopData.l1bridgeAddr).sendToL2(
                                      hopData.toChainId,
                                      hopData.receiverAddress,
                                      amount,
                                      hopData.amountOutMin,
                                      hopData.deadline,
                                      hopData.relayer,
                                      hopData.relayerFee
                                  );
                              }
                              emit SocketBridge(
                                  amount,
                                  hopData.token,
                                  hopData.toChainId,
                                  HopIdentifier,
                                  msg.sender,
                                  hopData.receiverAddress,
                                  hopData.metadata
                              );
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in HopBridgeData struct
                           * @param swapId routeId for the swapImpl
                           * @param swapData encoded data for swap
                           * @param hopData encoded data for HopData
                           */
                          function swapAndBridge(
                              uint32 swapId,
                              bytes calldata swapData,
                              HopDataNoToken calldata hopData
                          ) external payable {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(swapId)
                                  .delegatecall(swapData);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              (uint256 bridgeAmount, address token) = abi.decode(
                                  result,
                                  (uint256, address)
                              );
                              if (token == NATIVE_TOKEN_ADDRESS) {
                                  IHopL1Bridge(hopData.l1bridgeAddr).sendToL2{value: bridgeAmount}(
                                      hopData.toChainId,
                                      hopData.receiverAddress,
                                      bridgeAmount,
                                      hopData.amountOutMin,
                                      hopData.deadline,
                                      hopData.relayer,
                                      hopData.relayerFee
                                  );
                              } else {
                                  ERC20(token).safeApprove(hopData.l1bridgeAddr, bridgeAmount);
                                  // perform bridging
                                  IHopL1Bridge(hopData.l1bridgeAddr).sendToL2(
                                      hopData.toChainId,
                                      hopData.receiverAddress,
                                      bridgeAmount,
                                      hopData.amountOutMin,
                                      hopData.deadline,
                                      hopData.relayer,
                                      hopData.relayerFee
                                  );
                              }
                              emit SocketBridge(
                                  bridgeAmount,
                                  token,
                                  hopData.toChainId,
                                  HopIdentifier,
                                  msg.sender,
                                  hopData.receiverAddress,
                                  hopData.metadata
                              );
                          }
                          /**
                           * @notice function to handle ERC20 bridging to receipent via Hop-L1-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param receiverAddress The address receiving funds at the destination
                           * @param token token being bridged
                           * @param l1bridgeAddr address of the Hop-L1-Bridge to handle bridging the tokens
                           * @param relayer The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.
                           * @param toChainId The chainId of the destination chain
                           * @param amount The amount being sent
                           * @param amountOutMin The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.
                           * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`.
                           * @param hopData extra data needed to build the tx
                           */
                          function bridgeERC20To(
                              address receiverAddress,
                              address token,
                              address l1bridgeAddr,
                              address relayer,
                              uint256 toChainId,
                              uint256 amount,
                              uint256 amountOutMin,
                              uint256 relayerFee,
                              HopERC20Data calldata hopData
                          ) external payable {
                              ERC20 tokenInstance = ERC20(token);
                              tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);
                              tokenInstance.safeApprove(l1bridgeAddr, amount);
                              // perform bridging
                              IHopL1Bridge(l1bridgeAddr).sendToL2(
                                  toChainId,
                                  receiverAddress,
                                  amount,
                                  amountOutMin,
                                  hopData.deadline,
                                  relayer,
                                  relayerFee
                              );
                              emit SocketBridge(
                                  amount,
                                  token,
                                  toChainId,
                                  HopIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  hopData.metadata
                              );
                          }
                          /**
                           * @notice function to handle Native bridging to receipent via Hop-L1-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param receiverAddress The address receiving funds at the destination
                           * @param l1bridgeAddr address of the Hop-L1-Bridge to handle bridging the tokens
                           * @param relayer The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.
                           * @param toChainId The chainId of the destination chain
                           * @param amount The amount being sent
                           * @param amountOutMin The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.
                           * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`.
                           * @param deadline The deadline for swapping in the destination AMM market. 0 if no swap is intended.
                           */
                          function bridgeNativeTo(
                              address receiverAddress,
                              address l1bridgeAddr,
                              address relayer,
                              uint256 toChainId,
                              uint256 amount,
                              uint256 amountOutMin,
                              uint256 relayerFee,
                              uint256 deadline,
                              bytes32 metadata
                          ) external payable {
                              IHopL1Bridge(l1bridgeAddr).sendToL2{value: amount}(
                                  toChainId,
                                  receiverAddress,
                                  amount,
                                  amountOutMin,
                                  deadline,
                                  relayer,
                                  relayerFee
                              );
                              emit SocketBridge(
                                  amount,
                                  NATIVE_TOKEN_ADDRESS,
                                  toChainId,
                                  HopIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import "../interfaces/amm.sol";
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {BridgeImplBase} from "../../BridgeImplBase.sol";
                      import {HOP} from "../../../static/RouteIdentifiers.sol";
                      /**
                       * @title Hop-L2 Route Implementation
                       * @notice This is the L2 implementation, so this is used when transferring from l2 to supported l2s
                       * Called via SocketGateway if the routeId in the request maps to the routeId of HopL2-Implementation
                       * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap
                       * RequestData is different to just bride and bridging chained with swap
                       * @author Socket dot tech.
                       */
                      contract HopImplL2 is BridgeImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable HopIdentifier = HOP;
                          /// @notice Function-selector for ERC20-token bridging on Hop-L2-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens
                          bytes4 public immutable HOP_L2_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeERC20To(address,address,address,uint256,uint256,(uint256,uint256,uint256,uint256,uint256,bytes32))"
                                  )
                              );
                          /// @notice Function-selector for Native bridging on Hop-L2-Route
                          /// @dev This function selector is to be used while building transaction-data to bridge Native tokens
                          bytes4 public immutable HOP_L2_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeNativeTo(address,address,uint256,uint256,uint256,uint256,uint256,uint256,uint256,bytes32)"
                                  )
                              );
                          bytes4 public immutable HOP_L2_SWAP_BRIDGE_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "swapAndBridge(uint32,bytes,(address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes32))"
                                  )
                              );
                          /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase
                          constructor(
                              address _socketGateway,
                              address _socketDeployFactory
                          ) BridgeImplBase(_socketGateway, _socketDeployFactory) {}
                          /// @notice Struct to be used as a input parameter for Bridging tokens via Hop-L2-route
                          /// @dev while building transactionData,values should be set in this sequence of properties in this struct
                          struct HopBridgeRequestData {
                              // fees passed to relayer
                              uint256 bonderFee;
                              // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.
                              uint256 amountOutMin;
                              // The deadline for swapping in the destination AMM market. 0 if no swap is intended.
                              uint256 deadline;
                              // Minimum amount expected to be received or bridged to destination
                              uint256 amountOutMinDestination;
                              // deadline for bridging to destination
                              uint256 deadlineDestination;
                              // socket offchain created hash
                              bytes32 metadata;
                          }
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct HopBridgeDataNoToken {
                              // The address receiving funds at the destination
                              address receiverAddress;
                              // AMM address of Hop on L2
                              address hopAMM;
                              // The chainId of the destination chain
                              uint256 toChainId;
                              // fees passed to relayer
                              uint256 bonderFee;
                              // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.
                              uint256 amountOutMin;
                              // The deadline for swapping in the destination AMM market. 0 if no swap is intended.
                              uint256 deadline;
                              // Minimum amount expected to be received or bridged to destination
                              uint256 amountOutMinDestination;
                              // deadline for bridging to destination
                              uint256 deadlineDestination;
                              // socket offchain created hash
                              bytes32 metadata;
                          }
                          struct HopBridgeData {
                              /// @notice address of token being bridged
                              address token;
                              // The address receiving funds at the destination
                              address receiverAddress;
                              // AMM address of Hop on L2
                              address hopAMM;
                              // The chainId of the destination chain
                              uint256 toChainId;
                              // fees passed to relayer
                              uint256 bonderFee;
                              // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.
                              uint256 amountOutMin;
                              // The deadline for swapping in the destination AMM market. 0 if no swap is intended.
                              uint256 deadline;
                              // Minimum amount expected to be received or bridged to destination
                              uint256 amountOutMinDestination;
                              // deadline for bridging to destination
                              uint256 deadlineDestination;
                              // socket offchain created hash
                              bytes32 metadata;
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in HopBridgeData struct
                           * @param amount amount of tokens being bridged. this can be ERC20 or native
                           * @param bridgeData encoded data for Hop-L2-Bridge
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable override {
                              HopBridgeData memory hopData = abi.decode(bridgeData, (HopBridgeData));
                              if (hopData.token == NATIVE_TOKEN_ADDRESS) {
                                  HopAMM(hopData.hopAMM).swapAndSend{value: amount}(
                                      hopData.toChainId,
                                      hopData.receiverAddress,
                                      amount,
                                      hopData.bonderFee,
                                      hopData.amountOutMin,
                                      hopData.deadline,
                                      hopData.amountOutMinDestination,
                                      hopData.deadlineDestination
                                  );
                              } else {
                                  // perform bridging
                                  HopAMM(hopData.hopAMM).swapAndSend(
                                      hopData.toChainId,
                                      hopData.receiverAddress,
                                      amount,
                                      hopData.bonderFee,
                                      hopData.amountOutMin,
                                      hopData.deadline,
                                      hopData.amountOutMinDestination,
                                      hopData.deadlineDestination
                                  );
                              }
                              emit SocketBridge(
                                  amount,
                                  hopData.token,
                                  hopData.toChainId,
                                  HopIdentifier,
                                  msg.sender,
                                  hopData.receiverAddress,
                                  hopData.metadata
                              );
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in HopBridgeData struct
                           * @param swapId routeId for the swapImpl
                           * @param swapData encoded data for swap
                           * @param hopData encoded data for HopData
                           */
                          function swapAndBridge(
                              uint32 swapId,
                              bytes calldata swapData,
                              HopBridgeDataNoToken calldata hopData
                          ) external payable {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(swapId)
                                  .delegatecall(swapData);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              (uint256 bridgeAmount, address token) = abi.decode(
                                  result,
                                  (uint256, address)
                              );
                              if (token == NATIVE_TOKEN_ADDRESS) {
                                  HopAMM(hopData.hopAMM).swapAndSend{value: bridgeAmount}(
                                      hopData.toChainId,
                                      hopData.receiverAddress,
                                      bridgeAmount,
                                      hopData.bonderFee,
                                      hopData.amountOutMin,
                                      hopData.deadline,
                                      hopData.amountOutMinDestination,
                                      hopData.deadlineDestination
                                  );
                              } else {
                                  // perform bridging
                                  HopAMM(hopData.hopAMM).swapAndSend(
                                      hopData.toChainId,
                                      hopData.receiverAddress,
                                      bridgeAmount,
                                      hopData.bonderFee,
                                      hopData.amountOutMin,
                                      hopData.deadline,
                                      hopData.amountOutMinDestination,
                                      hopData.deadlineDestination
                                  );
                              }
                              emit SocketBridge(
                                  bridgeAmount,
                                  token,
                                  hopData.toChainId,
                                  HopIdentifier,
                                  msg.sender,
                                  hopData.receiverAddress,
                                  hopData.metadata
                              );
                          }
                          /**
                           * @notice function to handle ERC20 bridging to receipent via Hop-L2-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param receiverAddress The address receiving funds at the destination
                           * @param token token being bridged
                           * @param hopAMM AMM address of Hop on L2
                           * @param amount The amount being bridged
                           * @param toChainId The chainId of the destination chain
                           * @param hopBridgeRequestData extraData for Bridging across Hop-L2
                           */
                          function bridgeERC20To(
                              address receiverAddress,
                              address token,
                              address hopAMM,
                              uint256 amount,
                              uint256 toChainId,
                              HopBridgeRequestData calldata hopBridgeRequestData
                          ) external payable {
                              ERC20 tokenInstance = ERC20(token);
                              tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);
                              HopAMM(hopAMM).swapAndSend(
                                  toChainId,
                                  receiverAddress,
                                  amount,
                                  hopBridgeRequestData.bonderFee,
                                  hopBridgeRequestData.amountOutMin,
                                  hopBridgeRequestData.deadline,
                                  hopBridgeRequestData.amountOutMinDestination,
                                  hopBridgeRequestData.deadlineDestination
                              );
                              emit SocketBridge(
                                  amount,
                                  token,
                                  toChainId,
                                  HopIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  hopBridgeRequestData.metadata
                              );
                          }
                          /**
                           * @notice function to handle Native bridging to receipent via Hop-L2-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param receiverAddress The address receiving funds at the destination
                           * @param hopAMM AMM address of Hop on L2
                           * @param amount The amount being bridged
                           * @param toChainId The chainId of the destination chain
                           * @param bonderFee fees passed to relayer
                           * @param amountOutMin The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.
                           * @param deadline The deadline for swapping in the destination AMM market. 0 if no swap is intended.
                           * @param amountOutMinDestination Minimum amount expected to be received or bridged to destination
                           * @param deadlineDestination deadline for bridging to destination
                           */
                          function bridgeNativeTo(
                              address receiverAddress,
                              address hopAMM,
                              uint256 amount,
                              uint256 toChainId,
                              uint256 bonderFee,
                              uint256 amountOutMin,
                              uint256 deadline,
                              uint256 amountOutMinDestination,
                              uint256 deadlineDestination,
                              bytes32 metadata
                          ) external payable {
                              // token address might not be indication thats why passed through extraData
                              // perform bridging
                              HopAMM(hopAMM).swapAndSend{value: amount}(
                                  toChainId,
                                  receiverAddress,
                                  amount,
                                  bonderFee,
                                  amountOutMin,
                                  deadline,
                                  amountOutMinDestination,
                                  deadlineDestination
                              );
                              emit SocketBridge(
                                  amount,
                                  NATIVE_TOKEN_ADDRESS,
                                  toChainId,
                                  HopIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import "./interfaces/hyphen.sol";
                      import "../BridgeImplBase.sol";
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {HYPHEN} from "../../static/RouteIdentifiers.sol";
                      /**
                       * @title Hyphen-Route Implementation
                       * @notice Route implementation with functions to bridge ERC20 and Native via Hyphen-Bridge
                       * Called via SocketGateway if the routeId in the request maps to the routeId of HyphenImplementation
                       * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap
                       * RequestData is different to just bride and bridging chained with swap
                       * @author Socket dot tech.
                       */
                      contract HyphenImpl is BridgeImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable HyphenIdentifier = HYPHEN;
                          /// @notice Function-selector for ERC20-token bridging on Hyphen-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens
                          bytes4 public immutable HYPHEN_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256("bridgeERC20To(uint256,bytes32,address,address,uint256)")
                              );
                          /// @notice Function-selector for Native bridging on Hyphen-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens
                          bytes4 public immutable HYPHEN_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(keccak256("bridgeNativeTo(uint256,bytes32,address,uint256)"));
                          bytes4 public immutable HYPHEN_SWAP_BRIDGE_SELECTOR =
                              bytes4(
                                  keccak256("swapAndBridge(uint32,bytes,(address,uint256,bytes32))")
                              );
                          /// @notice liquidityPoolManager - liquidityPool Manager of Hyphen used to bridge ERC20 and native
                          /// @dev this is to be initialized in constructor with a valid deployed address of hyphen-liquidityPoolManager
                          HyphenLiquidityPoolManager public immutable liquidityPoolManager;
                          /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase
                          /// @dev ensure liquidityPoolManager-address are set properly for the chainId in which the contract is being deployed
                          constructor(
                              address _liquidityPoolManager,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) BridgeImplBase(_socketGateway, _socketDeployFactory) {
                              liquidityPoolManager = HyphenLiquidityPoolManager(
                                  _liquidityPoolManager
                              );
                          }
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct HyphenData {
                              /// @notice address of token being bridged
                              address token;
                              /// @notice address of receiver
                              address receiverAddress;
                              /// @notice chainId of destination
                              uint256 toChainId;
                              /// @notice socket offchain created hash
                              bytes32 metadata;
                          }
                          struct HyphenDataNoToken {
                              /// @notice address of receiver
                              address receiverAddress;
                              /// @notice chainId of destination
                              uint256 toChainId;
                              /// @notice chainId of destination
                              bytes32 metadata;
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in HyphenBridgeData struct
                           * @param amount amount of tokens being bridged. this can be ERC20 or native
                           * @param bridgeData encoded data for HyphenBridge
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable override {
                              HyphenData memory hyphenData = abi.decode(bridgeData, (HyphenData));
                              if (hyphenData.token == NATIVE_TOKEN_ADDRESS) {
                                  liquidityPoolManager.depositNative{value: amount}(
                                      hyphenData.receiverAddress,
                                      hyphenData.toChainId,
                                      "SOCKET"
                                  );
                              } else {
                                  ERC20(hyphenData.token).safeApprove(
                                      address(liquidityPoolManager),
                                      amount
                                  );
                                  liquidityPoolManager.depositErc20(
                                      hyphenData.toChainId,
                                      hyphenData.token,
                                      hyphenData.receiverAddress,
                                      amount,
                                      "SOCKET"
                                  );
                              }
                              emit SocketBridge(
                                  amount,
                                  hyphenData.token,
                                  hyphenData.toChainId,
                                  HyphenIdentifier,
                                  msg.sender,
                                  hyphenData.receiverAddress,
                                  hyphenData.metadata
                              );
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in HyphenBridgeData struct
                           * @param swapId routeId for the swapImpl
                           * @param swapData encoded data for swap
                           * @param hyphenData encoded data for hyphenData
                           */
                          function swapAndBridge(
                              uint32 swapId,
                              bytes calldata swapData,
                              HyphenDataNoToken calldata hyphenData
                          ) external payable {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(swapId)
                                  .delegatecall(swapData);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              (uint256 bridgeAmount, address token) = abi.decode(
                                  result,
                                  (uint256, address)
                              );
                              if (token == NATIVE_TOKEN_ADDRESS) {
                                  liquidityPoolManager.depositNative{value: bridgeAmount}(
                                      hyphenData.receiverAddress,
                                      hyphenData.toChainId,
                                      "SOCKET"
                                  );
                              } else {
                                  ERC20(token).safeApprove(
                                      address(liquidityPoolManager),
                                      bridgeAmount
                                  );
                                  liquidityPoolManager.depositErc20(
                                      hyphenData.toChainId,
                                      token,
                                      hyphenData.receiverAddress,
                                      bridgeAmount,
                                      "SOCKET"
                                  );
                              }
                              emit SocketBridge(
                                  bridgeAmount,
                                  token,
                                  hyphenData.toChainId,
                                  HyphenIdentifier,
                                  msg.sender,
                                  hyphenData.receiverAddress,
                                  hyphenData.metadata
                              );
                          }
                          /**
                           * @notice function to handle ERC20 bridging to receipent via Hyphen-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param amount amount to be sent
                           * @param receiverAddress address of the token to bridged to the destination chain.
                           * @param token address of token being bridged
                           * @param toChainId chainId of destination
                           */
                          function bridgeERC20To(
                              uint256 amount,
                              bytes32 metadata,
                              address receiverAddress,
                              address token,
                              uint256 toChainId
                          ) external payable {
                              ERC20 tokenInstance = ERC20(token);
                              tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);
                              tokenInstance.safeApprove(address(liquidityPoolManager), amount);
                              liquidityPoolManager.depositErc20(
                                  toChainId,
                                  token,
                                  receiverAddress,
                                  amount,
                                  "SOCKET"
                              );
                              emit SocketBridge(
                                  amount,
                                  token,
                                  toChainId,
                                  HyphenIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                          /**
                           * @notice function to handle Native bridging to receipent via Hyphen-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param amount amount to be sent
                           * @param receiverAddress address of the token to bridged to the destination chain.
                           * @param toChainId chainId of destination
                           */
                          function bridgeNativeTo(
                              uint256 amount,
                              bytes32 metadata,
                              address receiverAddress,
                              uint256 toChainId
                          ) external payable {
                              liquidityPoolManager.depositNative{value: amount}(
                                  receiverAddress,
                                  toChainId,
                                  "SOCKET"
                              );
                              emit SocketBridge(
                                  amount,
                                  NATIVE_TOKEN_ADDRESS,
                                  toChainId,
                                  HyphenIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                      }
                      // SPDX-License-Identifier: Apache-2.0
                      pragma solidity >=0.8.0;
                      /**
                       * @title HyphenLiquidityPoolManager
                       * @notice interface with functions to bridge ERC20 and Native via Hyphen-Bridge
                       * @author Socket dot tech.
                       */
                      interface HyphenLiquidityPoolManager {
                          /**
                           * @dev Function used to deposit tokens into pool to initiate a cross chain token transfer.
                           * @param toChainId Chain id where funds needs to be transfered
                           * @param tokenAddress ERC20 Token address that needs to be transfered
                           * @param receiver Address on toChainId where tokens needs to be transfered
                           * @param amount Amount of token being transfered
                           */
                          function depositErc20(
                              uint256 toChainId,
                              address tokenAddress,
                              address receiver,
                              uint256 amount,
                              string calldata tag
                          ) external;
                          /**
                           * @dev Function used to deposit native token into pool to initiate a cross chain token transfer.
                           * @param receiver Address on toChainId where tokens needs to be transfered
                           * @param toChainId Chain id where funds needs to be transfered
                           */
                          function depositNative(
                              address receiver,
                              uint256 toChainId,
                              string calldata tag
                          ) external payable;
                      }
                      // SPDX-License-Identifier: Apache-2.0
                      pragma solidity >=0.8.0;
                      interface L1StandardBridge {
                          /**
                           * @dev Performs the logic for deposits by storing the ETH and informing the L2 ETH Gateway of
                           * the deposit.
                           * @param _to Account to give the deposit to on L2.
                           * @param _l2Gas Gas limit required to complete the deposit on L2.
                           * @param _data Optional data to forward to L2. This data is provided
                           *        solely as a convenience for external contracts. Aside from enforcing a maximum
                           *        length, these contracts provide no guarantees about its content.
                           */
                          function depositETHTo(
                              address _to,
                              uint32 _l2Gas,
                              bytes calldata _data
                          ) external payable;
                          /**
                           * @dev deposit an amount of ERC20 to a recipient's balance on L2.
                           * @param _l1Token Address of the L1 ERC20 we are depositing
                           * @param _l2Token Address of the L1 respective L2 ERC20
                           * @param _to L2 address to credit the withdrawal to.
                           * @param _amount Amount of the ERC20 to deposit.
                           * @param _l2Gas Gas limit required to complete the deposit on L2.
                           * @param _data Optional data to forward to L2. This data is provided
                           *        solely as a convenience for external contracts. Aside from enforcing a maximum
                           *        length, these contracts provide no guarantees about its content.
                           */
                          function depositERC20To(
                              address _l1Token,
                              address _l2Token,
                              address _to,
                              uint256 _amount,
                              uint32 _l2Gas,
                              bytes calldata _data
                          ) external;
                      }
                      interface OldL1TokenGateway {
                          /**
                           * @dev Transfer SNX to L2 First, moves the SNX into the deposit escrow
                           *
                           * @param _to Account to give the deposit to on L2
                           * @param _amount Amount of the ERC20 to deposit.
                           */
                          function depositTo(address _to, uint256 _amount) external;
                          /**
                           * @dev Transfer SNX to L2 First, moves the SNX into the deposit escrow
                           *
                           * @param currencyKey currencyKey for the SynthToken
                           * @param destination Account to give the deposit to on L2
                           * @param amount Amount of the ERC20 to deposit.
                           */
                          function initiateSynthTransfer(
                              bytes32 currencyKey,
                              address destination,
                              uint256 amount
                          ) external;
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.0;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import "../interfaces/optimism.sol";
                      import {BridgeImplBase} from "../../BridgeImplBase.sol";
                      import {UnsupportedInterfaceId} from "../../../errors/SocketErrors.sol";
                      import {NATIVE_OPTIMISM} from "../../../static/RouteIdentifiers.sol";
                      /**
                       * @title NativeOptimism-Route Implementation
                       * @notice Route implementation with functions to bridge ERC20 and Native via NativeOptimism-Bridge
                       * Tokens are bridged from Ethereum to Optimism Chain.
                       * Called via SocketGateway if the routeId in the request maps to the routeId of NativeOptimism-Implementation
                       * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap
                       * RequestData is different to just bride and bridging chained with swap
                       * @author Socket dot tech.
                       */
                      contract NativeOptimismImpl is BridgeImplBase {
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable NativeOptimismIdentifier = NATIVE_OPTIMISM;
                          uint256 public constant DESTINATION_CHAIN_ID = 10;
                          /// @notice Function-selector for ERC20-token bridging on Native-Optimism-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens
                          bytes4
                              public immutable NATIVE_OPTIMISM_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeERC20To(address,address,address,uint32,(bytes32,bytes32),uint256,uint256,address,bytes)"
                                  )
                              );
                          /// @notice Function-selector for Native bridging on Native-Optimism-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge Native balance
                          bytes4
                              public immutable NATIVE_OPTIMISM_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeNativeTo(address,address,uint32,uint256,bytes32,bytes)"
                                  )
                              );
                          bytes4 public immutable NATIVE_OPTIMISM_SWAP_BRIDGE_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "swapAndBridge(uint32,bytes,(uint256,bytes32,bytes32,address,address,uint32,address,bytes))"
                                  )
                              );
                          /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase
                          constructor(
                              address _socketGateway,
                              address _socketDeployFactory
                          ) BridgeImplBase(_socketGateway, _socketDeployFactory) {}
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct OptimismBridgeDataNoToken {
                              // interfaceId to be set offchain which is used to select one of the 3 kinds of bridging (standard bridge / old standard / synthetic)
                              uint256 interfaceId;
                              // currencyKey of the token beingBridged
                              bytes32 currencyKey;
                              // socket offchain created hash
                              bytes32 metadata;
                              // address of receiver of bridged tokens
                              address receiverAddress;
                              /**
                               * OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token
                               * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)
                               */
                              address customBridgeAddress;
                              // Gas limit required to complete the deposit on L2.
                              uint32 l2Gas;
                              // Address of the L1 respective L2 ERC20
                              address l2Token;
                              // additional data , for ll contracts this will be 0x data or empty data
                              bytes data;
                          }
                          struct OptimismBridgeData {
                              // interfaceId to be set offchain which is used to select one of the 3 kinds of bridging (standard bridge / old standard / synthetic)
                              uint256 interfaceId;
                              // currencyKey of the token beingBridged
                              bytes32 currencyKey;
                              // socket offchain created hash
                              bytes32 metadata;
                              // address of receiver of bridged tokens
                              address receiverAddress;
                              /**
                               * OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token
                               * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)
                               */
                              address customBridgeAddress;
                              /// @notice address of token being bridged
                              address token;
                              // Gas limit required to complete the deposit on L2.
                              uint32 l2Gas;
                              // Address of the L1 respective L2 ERC20
                              address l2Token;
                              // additional data , for ll contracts this will be 0x data or empty data
                              bytes data;
                          }
                          struct OptimismERC20Data {
                              bytes32 currencyKey;
                              bytes32 metadata;
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in OptimismBridgeData struct
                           * @param amount amount of tokens being bridged. this can be ERC20 or native
                           * @param bridgeData encoded data for Optimism-Bridge
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable override {
                              OptimismBridgeData memory optimismBridgeData = abi.decode(
                                  bridgeData,
                                  (OptimismBridgeData)
                              );
                              emit SocketBridge(
                                  amount,
                                  optimismBridgeData.token,
                                  DESTINATION_CHAIN_ID,
                                  NativeOptimismIdentifier,
                                  msg.sender,
                                  optimismBridgeData.receiverAddress,
                                  optimismBridgeData.metadata
                              );
                              if (optimismBridgeData.token == NATIVE_TOKEN_ADDRESS) {
                                  L1StandardBridge(optimismBridgeData.customBridgeAddress)
                                      .depositETHTo{value: amount}(
                                      optimismBridgeData.receiverAddress,
                                      optimismBridgeData.l2Gas,
                                      optimismBridgeData.data
                                  );
                              } else {
                                  if (optimismBridgeData.interfaceId == 0) {
                                      revert UnsupportedInterfaceId();
                                  }
                                  ERC20(optimismBridgeData.token).safeApprove(
                                      optimismBridgeData.customBridgeAddress,
                                      amount
                                  );
                                  if (optimismBridgeData.interfaceId == 1) {
                                      // deposit into standard bridge
                                      L1StandardBridge(optimismBridgeData.customBridgeAddress)
                                          .depositERC20To(
                                              optimismBridgeData.token,
                                              optimismBridgeData.l2Token,
                                              optimismBridgeData.receiverAddress,
                                              amount,
                                              optimismBridgeData.l2Gas,
                                              optimismBridgeData.data
                                          );
                                      return;
                                  }
                                  // Deposit Using Old Standard - iOVM_L1TokenGateway(Example - SNX Token)
                                  if (optimismBridgeData.interfaceId == 2) {
                                      OldL1TokenGateway(optimismBridgeData.customBridgeAddress)
                                          .depositTo(optimismBridgeData.receiverAddress, amount);
                                      return;
                                  }
                                  if (optimismBridgeData.interfaceId == 3) {
                                      OldL1TokenGateway(optimismBridgeData.customBridgeAddress)
                                          .initiateSynthTransfer(
                                              optimismBridgeData.currencyKey,
                                              optimismBridgeData.receiverAddress,
                                              amount
                                          );
                                      return;
                                  }
                              }
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in OptimismBridgeData struct
                           * @param swapId routeId for the swapImpl
                           * @param swapData encoded data for swap
                           * @param optimismBridgeData encoded data for OptimismBridgeData
                           */
                          function swapAndBridge(
                              uint32 swapId,
                              bytes calldata swapData,
                              OptimismBridgeDataNoToken calldata optimismBridgeData
                          ) external payable {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(swapId)
                                  .delegatecall(swapData);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              (uint256 bridgeAmount, address token) = abi.decode(
                                  result,
                                  (uint256, address)
                              );
                              emit SocketBridge(
                                  bridgeAmount,
                                  token,
                                  DESTINATION_CHAIN_ID,
                                  NativeOptimismIdentifier,
                                  msg.sender,
                                  optimismBridgeData.receiverAddress,
                                  optimismBridgeData.metadata
                              );
                              if (token == NATIVE_TOKEN_ADDRESS) {
                                  L1StandardBridge(optimismBridgeData.customBridgeAddress)
                                      .depositETHTo{value: bridgeAmount}(
                                      optimismBridgeData.receiverAddress,
                                      optimismBridgeData.l2Gas,
                                      optimismBridgeData.data
                                  );
                              } else {
                                  if (optimismBridgeData.interfaceId == 0) {
                                      revert UnsupportedInterfaceId();
                                  }
                                  ERC20(token).safeApprove(
                                      optimismBridgeData.customBridgeAddress,
                                      bridgeAmount
                                  );
                                  if (optimismBridgeData.interfaceId == 1) {
                                      // deposit into standard bridge
                                      L1StandardBridge(optimismBridgeData.customBridgeAddress)
                                          .depositERC20To(
                                              token,
                                              optimismBridgeData.l2Token,
                                              optimismBridgeData.receiverAddress,
                                              bridgeAmount,
                                              optimismBridgeData.l2Gas,
                                              optimismBridgeData.data
                                          );
                                      return;
                                  }
                                  // Deposit Using Old Standard - iOVM_L1TokenGateway(Example - SNX Token)
                                  if (optimismBridgeData.interfaceId == 2) {
                                      OldL1TokenGateway(optimismBridgeData.customBridgeAddress)
                                          .depositTo(
                                              optimismBridgeData.receiverAddress,
                                              bridgeAmount
                                          );
                                      return;
                                  }
                                  if (optimismBridgeData.interfaceId == 3) {
                                      OldL1TokenGateway(optimismBridgeData.customBridgeAddress)
                                          .initiateSynthTransfer(
                                              optimismBridgeData.currencyKey,
                                              optimismBridgeData.receiverAddress,
                                              bridgeAmount
                                          );
                                      return;
                                  }
                              }
                          }
                          /**
                           * @notice function to handle ERC20 bridging to receipent via NativeOptimism-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param token address of token being bridged
                           * @param receiverAddress address of receiver of bridged tokens
                           * @param customBridgeAddress OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token
                           *                           contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)
                           * @param l2Gas Gas limit required to complete the deposit on L2.
                           * @param optimismData extra data needed for optimism bridge
                           * @param amount amount being bridged
                           * @param interfaceId interfaceId to be set offchain which is used to select one of the 3 kinds of bridging (standard bridge / old standard / synthetic)
                           * @param l2Token Address of the L1 respective L2 ERC20
                           * @param data additional data , for ll contracts this will be 0x data or empty data
                           */
                          function bridgeERC20To(
                              address token,
                              address receiverAddress,
                              address customBridgeAddress,
                              uint32 l2Gas,
                              OptimismERC20Data calldata optimismData,
                              uint256 amount,
                              uint256 interfaceId,
                              address l2Token,
                              bytes calldata data
                          ) external payable {
                              if (interfaceId == 0) {
                                  revert UnsupportedInterfaceId();
                              }
                              ERC20 tokenInstance = ERC20(token);
                              tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);
                              tokenInstance.safeApprove(customBridgeAddress, amount);
                              emit SocketBridge(
                                  amount,
                                  token,
                                  DESTINATION_CHAIN_ID,
                                  NativeOptimismIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  optimismData.metadata
                              );
                              if (interfaceId == 1) {
                                  // deposit into standard bridge
                                  L1StandardBridge(customBridgeAddress).depositERC20To(
                                      token,
                                      l2Token,
                                      receiverAddress,
                                      amount,
                                      l2Gas,
                                      data
                                  );
                                  return;
                              }
                              // Deposit Using Old Standard - iOVM_L1TokenGateway(Example - SNX Token)
                              if (interfaceId == 2) {
                                  OldL1TokenGateway(customBridgeAddress).depositTo(
                                      receiverAddress,
                                      amount
                                  );
                                  return;
                              }
                              if (interfaceId == 3) {
                                  OldL1TokenGateway(customBridgeAddress).initiateSynthTransfer(
                                      optimismData.currencyKey,
                                      receiverAddress,
                                      amount
                                  );
                                  return;
                              }
                          }
                          /**
                           * @notice function to handle native balance bridging to receipent via NativeOptimism-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param receiverAddress address of receiver of bridged tokens
                           * @param customBridgeAddress OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token
                           *                           contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)
                           * @param l2Gas Gas limit required to complete the deposit on L2.
                           * @param amount amount being bridged
                           * @param data additional data , for ll contracts this will be 0x data or empty data
                           */
                          function bridgeNativeTo(
                              address receiverAddress,
                              address customBridgeAddress,
                              uint32 l2Gas,
                              uint256 amount,
                              bytes32 metadata,
                              bytes calldata data
                          ) external payable {
                              L1StandardBridge(customBridgeAddress).depositETHTo{value: amount}(
                                  receiverAddress,
                                  l2Gas,
                                  data
                              );
                              emit SocketBridge(
                                  amount,
                                  NATIVE_TOKEN_ADDRESS,
                                  DESTINATION_CHAIN_ID,
                                  NativeOptimismIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.0;
                      /**
                       * @title RootChain Manager Interface for Polygon Bridge.
                       */
                      interface IRootChainManager {
                          /**
                           * @notice Move ether from root to child chain, accepts ether transfer
                           * Keep in mind this ether cannot be used to pay gas on child chain
                           * Use Matic tokens deposited using plasma mechanism for that
                           * @param user address of account that should receive WETH on child chain
                           */
                          function depositEtherFor(address user) external payable;
                          /**
                           * @notice Move tokens from root to child chain
                           * @dev This mechanism supports arbitrary tokens as long as its predicate has been registered and the token is mapped
                           * @param sender address of account that should receive this deposit on child chain
                           * @param token address of token that is being deposited
                           * @param extraData bytes data that is sent to predicate and child token contracts to handle deposit
                           */
                          function depositFor(
                              address sender,
                              address token,
                              bytes memory extraData
                          ) external;
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.0;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import "./interfaces/polygon.sol";
                      import {BridgeImplBase} from "../BridgeImplBase.sol";
                      import {NATIVE_POLYGON} from "../../static/RouteIdentifiers.sol";
                      /**
                       * @title NativePolygon-Route Implementation
                       * @notice This is the L1 implementation, so this is used when transferring from ethereum to polygon via their native bridge.
                       * @author Socket dot tech.
                       */
                      contract NativePolygonImpl is BridgeImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable NativePolyonIdentifier = NATIVE_POLYGON;
                          /// @notice destination-chain-Id for this router is always arbitrum
                          uint256 public constant DESTINATION_CHAIN_ID = 137;
                          /// @notice Function-selector for ERC20-token bridging on NativePolygon-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens
                          bytes4
                              public immutable NATIVE_POLYGON_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(keccak256("bridgeERC20To(uint256,bytes32,address,address)"));
                          /// @notice Function-selector for Native bridging on NativePolygon-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens
                          bytes4
                              public immutable NATIVE_POLYGON_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(keccak256("bridgeNativeTo(uint256,bytes32,address)"));
                          bytes4 public immutable NATIVE_POLYGON_SWAP_BRIDGE_SELECTOR =
                              bytes4(keccak256("swapAndBridge(uint32,address,bytes32,bytes)"));
                          /// @notice root chain manager proxy on the ethereum chain
                          /// @dev to be initialised in the constructor
                          IRootChainManager public immutable rootChainManagerProxy;
                          /// @notice ERC20 Predicate proxy on the ethereum chain
                          /// @dev to be initialised in the constructor
                          address public immutable erc20PredicateProxy;
                          /**
                           * // @notice We set all the required addresses in the constructor while deploying the contract.
                           * // These will be constant addresses.
                           * // @dev Please use the Proxy addresses and not the implementation addresses while setting these
                           * // @param _rootChainManagerProxy address of the root chain manager proxy on the ethereum chain
                           * // @param _erc20PredicateProxy address of the ERC20 Predicate proxy on the ethereum chain.
                           * // @param _socketGateway address of the socketGateway contract that calls this contract
                           */
                          constructor(
                              address _rootChainManagerProxy,
                              address _erc20PredicateProxy,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) BridgeImplBase(_socketGateway, _socketDeployFactory) {
                              rootChainManagerProxy = IRootChainManager(_rootChainManagerProxy);
                              erc20PredicateProxy = _erc20PredicateProxy;
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in NativePolygon-BridgeData struct
                           * @param amount amount of tokens being bridged. this can be ERC20 or native
                           * @param bridgeData encoded data for NativePolygon-Bridge
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable override {
                              (address token, address receiverAddress, bytes32 metadata) = abi.decode(
                                  bridgeData,
                                  (address, address, bytes32)
                              );
                              if (token == NATIVE_TOKEN_ADDRESS) {
                                  IRootChainManager(rootChainManagerProxy).depositEtherFor{
                                      value: amount
                                  }(receiverAddress);
                              } else {
                                  ERC20(token).safeApprove(erc20PredicateProxy, amount);
                                  // deposit into rootchain manager
                                  IRootChainManager(rootChainManagerProxy).depositFor(
                                      receiverAddress,
                                      token,
                                      abi.encodePacked(amount)
                                  );
                              }
                              emit SocketBridge(
                                  amount,
                                  token,
                                  DESTINATION_CHAIN_ID,
                                  NativePolyonIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in NativePolygon-BridgeData struct
                           * @param swapId routeId for the swapImpl
                           * @param receiverAddress address of the receiver
                           * @param swapData encoded data for swap
                           */
                          function swapAndBridge(
                              uint32 swapId,
                              address receiverAddress,
                              bytes32 metadata,
                              bytes calldata swapData
                          ) external payable {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(swapId)
                                  .delegatecall(swapData);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              (uint256 bridgeAmount, address token) = abi.decode(
                                  result,
                                  (uint256, address)
                              );
                              if (token == NATIVE_TOKEN_ADDRESS) {
                                  IRootChainManager(rootChainManagerProxy).depositEtherFor{
                                      value: bridgeAmount
                                  }(receiverAddress);
                              } else {
                                  ERC20(token).safeApprove(erc20PredicateProxy, bridgeAmount);
                                  // deposit into rootchain manager
                                  IRootChainManager(rootChainManagerProxy).depositFor(
                                      receiverAddress,
                                      token,
                                      abi.encodePacked(bridgeAmount)
                                  );
                              }
                              emit SocketBridge(
                                  bridgeAmount,
                                  token,
                                  DESTINATION_CHAIN_ID,
                                  NativePolyonIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                          /**
                           * @notice function to handle ERC20 bridging to receipent via NativePolygon-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param amount amount of tokens being bridged
                           * @param receiverAddress recipient address
                           * @param token address of token being bridged
                           */
                          function bridgeERC20To(
                              uint256 amount,
                              bytes32 metadata,
                              address receiverAddress,
                              address token
                          ) external payable {
                              ERC20 tokenInstance = ERC20(token);
                              // set allowance for erc20 predicate
                              tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);
                              tokenInstance.safeApprove(erc20PredicateProxy, amount);
                              // deposit into rootchain manager
                              rootChainManagerProxy.depositFor(
                                  receiverAddress,
                                  token,
                                  abi.encodePacked(amount)
                              );
                              emit SocketBridge(
                                  amount,
                                  token,
                                  DESTINATION_CHAIN_ID,
                                  NativePolyonIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                          /**
                           * @notice function to handle Native bridging to receipent via NativePolygon-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param amount amount of tokens being bridged
                           * @param receiverAddress recipient address
                           */
                          function bridgeNativeTo(
                              uint256 amount,
                              bytes32 metadata,
                              address receiverAddress
                          ) external payable {
                              rootChainManagerProxy.depositEtherFor{value: amount}(receiverAddress);
                              emit SocketBridge(
                                  amount,
                                  NATIVE_TOKEN_ADDRESS,
                                  DESTINATION_CHAIN_ID,
                                  NativePolyonIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                      }
                      // SPDX-License-Identifier: Apache-2.0
                      pragma solidity >=0.8.0;
                      /// @notice interface with functions to interact with Refuel contract
                      interface IRefuel {
                          /**
                           * @notice function to deposit nativeToken to Destination-address on destinationChain
                           * @param destinationChainId chainId of the Destination chain
                           * @param _to recipient address
                           */
                          function depositNativeToken(
                              uint256 destinationChainId,
                              address _to
                          ) external payable;
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import "./interfaces/refuel.sol";
                      import "../BridgeImplBase.sol";
                      import {REFUEL} from "../../static/RouteIdentifiers.sol";
                      /**
                       * @title Refuel-Route Implementation
                       * @notice Route implementation with functions to bridge Native via Refuel-Bridge
                       * Called via SocketGateway if the routeId in the request maps to the routeId of RefuelImplementation
                       * @author Socket dot tech.
                       */
                      contract RefuelBridgeImpl is BridgeImplBase {
                          bytes32 public immutable RefuelIdentifier = REFUEL;
                          /// @notice refuelBridge-Contract address used to deposit Native on Refuel-Bridge
                          address public immutable refuelBridge;
                          /// @notice Function-selector for Native bridging via Refuel-Bridge
                          /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens
                          bytes4 public immutable REFUEL_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(keccak256("bridgeNativeTo(uint256,address,uint256,bytes32)"));
                          bytes4 public immutable REFUEL_NATIVE_SWAP_BRIDGE_SELECTOR =
                              bytes4(
                                  keccak256("swapAndBridge(uint32,address,uint256,bytes32,bytes)")
                              );
                          /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase
                          /// @dev ensure _refuelBridge are set properly for the chainId in which the contract is being deployed
                          constructor(
                              address _refuelBridge,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) BridgeImplBase(_socketGateway, _socketDeployFactory) {
                              refuelBridge = _refuelBridge;
                          }
                          // Function to receive Ether. msg.data must be empty
                          receive() external payable {}
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct RefuelBridgeData {
                              address receiverAddress;
                              uint256 toChainId;
                              bytes32 metadata;
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in RefuelBridgeData struct
                           * @param amount amount of tokens being bridged. this must be only native
                           * @param bridgeData encoded data for RefuelBridge
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable override {
                              RefuelBridgeData memory refuelBridgeData = abi.decode(
                                  bridgeData,
                                  (RefuelBridgeData)
                              );
                              IRefuel(refuelBridge).depositNativeToken{value: amount}(
                                  refuelBridgeData.toChainId,
                                  refuelBridgeData.receiverAddress
                              );
                              emit SocketBridge(
                                  amount,
                                  NATIVE_TOKEN_ADDRESS,
                                  refuelBridgeData.toChainId,
                                  RefuelIdentifier,
                                  msg.sender,
                                  refuelBridgeData.receiverAddress,
                                  refuelBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in RefuelBridgeData struct
                           * @param swapId routeId for the swapImpl
                           * @param receiverAddress receiverAddress
                           * @param toChainId toChainId
                           * @param swapData encoded data for swap
                           */
                          function swapAndBridge(
                              uint32 swapId,
                              address receiverAddress,
                              uint256 toChainId,
                              bytes32 metadata,
                              bytes calldata swapData
                          ) external payable {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(swapId)
                                  .delegatecall(swapData);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              (uint256 bridgeAmount, ) = abi.decode(result, (uint256, address));
                              IRefuel(refuelBridge).depositNativeToken{value: bridgeAmount}(
                                  toChainId,
                                  receiverAddress
                              );
                              emit SocketBridge(
                                  bridgeAmount,
                                  NATIVE_TOKEN_ADDRESS,
                                  toChainId,
                                  RefuelIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                          /**
                           * @notice function to handle Native bridging to receipent via Refuel-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param amount amount of native being refuelled to destination chain
                           * @param receiverAddress recipient address of the refuelled native
                           * @param toChainId destinationChainId
                           */
                          function bridgeNativeTo(
                              uint256 amount,
                              address receiverAddress,
                              uint256 toChainId,
                              bytes32 metadata
                          ) external payable {
                              IRefuel(refuelBridge).depositNativeToken{value: amount}(
                                  toChainId,
                                  receiverAddress
                              );
                              emit SocketBridge(
                                  amount,
                                  NATIVE_TOKEN_ADDRESS,
                                  toChainId,
                                  RefuelIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                      }
                      // SPDX-License-Identifier: GPL-3.0-only
                      pragma solidity >=0.8.0;
                      /**
                       * @title IBridgeStargate Interface Contract.
                       * @notice Interface used by Stargate-L1 and L2 Router implementations
                       * @dev router and routerETH addresses will be distinct for L1 and L2
                       */
                      interface IBridgeStargate {
                          // @notice Struct to hold the additional-data for bridging ERC20 token
                          struct lzTxObj {
                              // gas limit to bridge the token in Stargate to destinationChain
                              uint256 dstGasForCall;
                              // destination nativeAmount, this is always set as 0
                              uint256 dstNativeAmount;
                              // destination nativeAddress, this is always set as 0x
                              bytes dstNativeAddr;
                          }
                          /// @notice function in stargate bridge which is used to bridge ERC20 tokens to recipient on destinationChain
                          function swap(
                              uint16 _dstChainId,
                              uint256 _srcPoolId,
                              uint256 _dstPoolId,
                              address payable _refundAddress,
                              uint256 _amountLD,
                              uint256 _minAmountLD,
                              lzTxObj memory _lzTxParams,
                              bytes calldata _to,
                              bytes calldata _payload
                          ) external payable;
                          /// @notice function in stargate bridge which is used to bridge native tokens to recipient on destinationChain
                          function swapETH(
                              uint16 _dstChainId, // destination Stargate chainId
                              address payable _refundAddress, // refund additional messageFee to this address
                              bytes calldata _toAddress, // the receiver of the destination ETH
                              uint256 _amountLD, // the amount, in Local Decimals, to be swapped
                              uint256 _minAmountLD // the minimum amount accepted out on destination
                          ) external payable;
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import "../interfaces/stargate.sol";
                      import {BridgeImplBase} from "../../BridgeImplBase.sol";
                      import {STARGATE} from "../../../static/RouteIdentifiers.sol";
                      /**
                       * @title Stargate-L1-Route Implementation
                       * @notice Route implementation with functions to bridge ERC20 and Native via Stargate-L1-Bridge
                       * Called via SocketGateway if the routeId in the request maps to the routeId of Stargate-L1-Implementation
                       * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap
                       * RequestData is different to just bride and bridging chained with swap
                       * @author Socket dot tech.
                       */
                      contract StargateImplL1 is BridgeImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable StargateIdentifier = STARGATE;
                          /// @notice Function-selector for ERC20-token bridging on Stargate-L1-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens
                          bytes4
                              public immutable STARGATE_L1_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeERC20To(address,address,address,uint256,uint256,(uint256,uint256,uint256,uint256,bytes32,bytes,uint16))"
                                  )
                              );
                          /// @notice Function-selector for Native bridging on Stargate-L1-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens
                          bytes4
                              public immutable STARGATE_L1_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeNativeTo(address,address,uint16,uint256,uint256,uint256,bytes32)"
                                  )
                              );
                          bytes4 public immutable STARGATE_L1_SWAP_BRIDGE_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "swapAndBridge(uint32,bytes,(address,address,uint16,uint256,uint256,uint256,uint256,uint256,uint256,bytes32,bytes))"
                                  )
                              );
                          /// @notice Stargate Router to bridge ERC20 tokens
                          IBridgeStargate public immutable router;
                          /// @notice Stargate Router to bridge native tokens
                          IBridgeStargate public immutable routerETH;
                          /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase
                          /// @dev ensure router, routerEth are set properly for the chainId in which the contract is being deployed
                          constructor(
                              address _router,
                              address _routerEth,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) BridgeImplBase(_socketGateway, _socketDeployFactory) {
                              router = IBridgeStargate(_router);
                              routerETH = IBridgeStargate(_routerEth);
                          }
                          struct StargateBridgeExtraData {
                              uint256 srcPoolId;
                              uint256 dstPoolId;
                              uint256 destinationGasLimit;
                              uint256 minReceivedAmt;
                              bytes32 metadata;
                              bytes destinationPayload;
                              uint16 stargateDstChainId; // stargate defines chain id in its way
                          }
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct StargateBridgeDataNoToken {
                              address receiverAddress;
                              address senderAddress;
                              uint16 stargateDstChainId; // stargate defines chain id in its way
                              uint256 value;
                              // a unique identifier that is uses to dedup transfers
                              // this value is the a timestamp sent from frontend, but in theory can be any unique number
                              uint256 srcPoolId;
                              uint256 dstPoolId;
                              uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination
                              uint256 optionalValue;
                              uint256 destinationGasLimit;
                              bytes32 metadata;
                              bytes destinationPayload;
                          }
                          struct StargateBridgeData {
                              address token;
                              address receiverAddress;
                              address senderAddress;
                              uint16 stargateDstChainId; // stargate defines chain id in its way
                              uint256 value;
                              // a unique identifier that is uses to dedup transfers
                              // this value is the a timestamp sent from frontend, but in theory can be any unique number
                              uint256 srcPoolId;
                              uint256 dstPoolId;
                              uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination
                              uint256 optionalValue;
                              uint256 destinationGasLimit;
                              bytes32 metadata;
                              bytes destinationPayload;
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct
                           * @param amount amount of tokens being bridged. this can be ERC20 or native
                           * @param bridgeData encoded data for Stargate-L1-Bridge
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable override {
                              StargateBridgeData memory stargateBridgeData = abi.decode(
                                  bridgeData,
                                  (StargateBridgeData)
                              );
                              if (stargateBridgeData.token == NATIVE_TOKEN_ADDRESS) {
                                  // perform bridging
                                  routerETH.swapETH{value: amount + stargateBridgeData.optionalValue}(
                                      stargateBridgeData.stargateDstChainId,
                                      payable(stargateBridgeData.senderAddress),
                                      abi.encodePacked(stargateBridgeData.receiverAddress),
                                      amount,
                                      stargateBridgeData.minReceivedAmt
                                  );
                              } else {
                                  ERC20(stargateBridgeData.token).safeApprove(
                                      address(router),
                                      amount
                                  );
                                  {
                                      router.swap{value: stargateBridgeData.value}(
                                          stargateBridgeData.stargateDstChainId,
                                          stargateBridgeData.srcPoolId,
                                          stargateBridgeData.dstPoolId,
                                          payable(stargateBridgeData.senderAddress), // default to refund to main contract
                                          amount,
                                          stargateBridgeData.minReceivedAmt,
                                          IBridgeStargate.lzTxObj(
                                              stargateBridgeData.destinationGasLimit,
                                              0, // zero amount since this is a ERC20 bridging
                                              "0x" //empty data since this is for only ERC20
                                          ),
                                          abi.encodePacked(stargateBridgeData.receiverAddress),
                                          stargateBridgeData.destinationPayload
                                      );
                                  }
                              }
                              emit SocketBridge(
                                  amount,
                                  stargateBridgeData.token,
                                  stargateBridgeData.stargateDstChainId,
                                  StargateIdentifier,
                                  msg.sender,
                                  stargateBridgeData.receiverAddress,
                                  stargateBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct
                           * @param swapId routeId for the swapImpl
                           * @param swapData encoded data for swap
                           * @param stargateBridgeData encoded data for StargateBridgeData
                           */
                          function swapAndBridge(
                              uint32 swapId,
                              bytes calldata swapData,
                              StargateBridgeDataNoToken calldata stargateBridgeData
                          ) external payable {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(swapId)
                                  .delegatecall(swapData);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              (uint256 bridgeAmount, address token) = abi.decode(
                                  result,
                                  (uint256, address)
                              );
                              if (token == NATIVE_TOKEN_ADDRESS) {
                                  // perform bridging
                                  routerETH.swapETH{
                                      value: bridgeAmount + stargateBridgeData.optionalValue
                                  }(
                                      stargateBridgeData.stargateDstChainId,
                                      payable(stargateBridgeData.senderAddress),
                                      abi.encodePacked(stargateBridgeData.receiverAddress),
                                      bridgeAmount,
                                      stargateBridgeData.minReceivedAmt
                                  );
                              } else {
                                  ERC20(token).safeApprove(address(router), bridgeAmount);
                                  {
                                      router.swap{value: stargateBridgeData.value}(
                                          stargateBridgeData.stargateDstChainId,
                                          stargateBridgeData.srcPoolId,
                                          stargateBridgeData.dstPoolId,
                                          payable(stargateBridgeData.senderAddress), // default to refund to main contract
                                          bridgeAmount,
                                          stargateBridgeData.minReceivedAmt,
                                          IBridgeStargate.lzTxObj(
                                              stargateBridgeData.destinationGasLimit,
                                              0, // zero amount since this is a ERC20 bridging
                                              "0x" //empty data since this is for only ERC20
                                          ),
                                          abi.encodePacked(stargateBridgeData.receiverAddress),
                                          stargateBridgeData.destinationPayload
                                      );
                                  }
                              }
                              emit SocketBridge(
                                  bridgeAmount,
                                  token,
                                  stargateBridgeData.stargateDstChainId,
                                  StargateIdentifier,
                                  msg.sender,
                                  stargateBridgeData.receiverAddress,
                                  stargateBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to handle ERC20 bridging to receipent via Stargate-L1-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param token address of token being bridged
                           * @param senderAddress address of sender
                           * @param receiverAddress address of recipient
                           * @param amount amount of token being bridge
                           * @param value value
                           * @param stargateBridgeExtraData stargate bridge extradata
                           */
                          function bridgeERC20To(
                              address token,
                              address senderAddress,
                              address receiverAddress,
                              uint256 amount,
                              uint256 value,
                              StargateBridgeExtraData calldata stargateBridgeExtraData
                          ) external payable {
                              ERC20 tokenInstance = ERC20(token);
                              tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);
                              tokenInstance.safeApprove(address(router), amount);
                              {
                                  router.swap{value: value}(
                                      stargateBridgeExtraData.stargateDstChainId,
                                      stargateBridgeExtraData.srcPoolId,
                                      stargateBridgeExtraData.dstPoolId,
                                      payable(senderAddress), // default to refund to main contract
                                      amount,
                                      stargateBridgeExtraData.minReceivedAmt,
                                      IBridgeStargate.lzTxObj(
                                          stargateBridgeExtraData.destinationGasLimit,
                                          0, // zero amount since this is a ERC20 bridging
                                          "0x" //empty data since this is for only ERC20
                                      ),
                                      abi.encodePacked(receiverAddress),
                                      stargateBridgeExtraData.destinationPayload
                                  );
                              }
                              emit SocketBridge(
                                  amount,
                                  token,
                                  stargateBridgeExtraData.stargateDstChainId,
                                  StargateIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  stargateBridgeExtraData.metadata
                              );
                          }
                          /**
                           * @notice function to handle Native bridging to receipent via Stargate-L1-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param receiverAddress address of receipient
                           * @param senderAddress address of sender
                           * @param stargateDstChainId stargate defines chain id in its way
                           * @param amount amount of token being bridge
                           * @param minReceivedAmt defines the slippage, the min qty you would accept on the destination
                           * @param optionalValue optionalValue Native amount
                           */
                          function bridgeNativeTo(
                              address receiverAddress,
                              address senderAddress,
                              uint16 stargateDstChainId,
                              uint256 amount,
                              uint256 minReceivedAmt,
                              uint256 optionalValue,
                              bytes32 metadata
                          ) external payable {
                              // perform bridging
                              routerETH.swapETH{value: amount + optionalValue}(
                                  stargateDstChainId,
                                  payable(senderAddress),
                                  abi.encodePacked(receiverAddress),
                                  amount,
                                  minReceivedAmt
                              );
                              emit SocketBridge(
                                  amount,
                                  NATIVE_TOKEN_ADDRESS,
                                  stargateDstChainId,
                                  StargateIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import "../interfaces/stargate.sol";
                      import "../../../errors/SocketErrors.sol";
                      import {BridgeImplBase} from "../../BridgeImplBase.sol";
                      import {STARGATE} from "../../../static/RouteIdentifiers.sol";
                      /**
                       * @title Stargate-L2-Route Implementation
                       * @notice Route implementation with functions to bridge ERC20 and Native via Stargate-L2-Bridge
                       * Called via SocketGateway if the routeId in the request maps to the routeId of Stargate-L2-Implementation
                       * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap
                       * RequestData is different to just bride and bridging chained with swap
                       * @author Socket dot tech.
                       */
                      contract StargateImplL2 is BridgeImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable StargateIdentifier = STARGATE;
                          /// @notice Function-selector for ERC20-token bridging on Stargate-L2-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens
                          bytes4
                              public immutable STARGATE_L2_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeERC20To(address,address,address,uint256,uint256,uint256,(uint256,uint256,uint256,uint256,bytes32,bytes,uint16))"
                                  )
                              );
                          bytes4 public immutable STARGATE_L1_SWAP_BRIDGE_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "swapAndBridge(uint32,bytes,(address,address,uint16,uint256,uint256,uint256,uint256,uint256,uint256,bytes32,bytes))"
                                  )
                              );
                          /// @notice Function-selector for Native bridging on Stargate-L2-Route
                          /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens
                          bytes4
                              public immutable STARGATE_L2_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "bridgeNativeTo(address,address,uint16,uint256,uint256,uint256,bytes32)"
                                  )
                              );
                          /// @notice Stargate Router to bridge ERC20 tokens
                          IBridgeStargate public immutable router;
                          /// @notice Stargate Router to bridge native tokens
                          IBridgeStargate public immutable routerETH;
                          /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase
                          /// @dev ensure router, routerEth are set properly for the chainId in which the contract is being deployed
                          constructor(
                              address _router,
                              address _routerEth,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) BridgeImplBase(_socketGateway, _socketDeployFactory) {
                              router = IBridgeStargate(_router);
                              routerETH = IBridgeStargate(_routerEth);
                          }
                          /// @notice Struct to be used as a input parameter for Bridging tokens via Stargate-L2-route
                          /// @dev while building transactionData,values should be set in this sequence of properties in this struct
                          struct StargateBridgeExtraData {
                              uint256 srcPoolId;
                              uint256 dstPoolId;
                              uint256 destinationGasLimit;
                              uint256 minReceivedAmt;
                              bytes32 metadata;
                              bytes destinationPayload;
                              uint16 stargateDstChainId; // stargate defines chain id in its way
                          }
                          /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.
                          /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct
                          struct StargateBridgeDataNoToken {
                              address receiverAddress;
                              address senderAddress;
                              uint16 stargateDstChainId; // stargate defines chain id in its way
                              uint256 value;
                              // a unique identifier that is uses to dedup transfers
                              // this value is the a timestamp sent from frontend, but in theory can be any unique number
                              uint256 srcPoolId;
                              uint256 dstPoolId;
                              uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination
                              uint256 optionalValue;
                              uint256 destinationGasLimit;
                              bytes32 metadata;
                              bytes destinationPayload;
                          }
                          struct StargateBridgeData {
                              address token;
                              address receiverAddress;
                              address senderAddress;
                              uint16 stargateDstChainId; // stargate defines chain id in its way
                              uint256 value;
                              // a unique identifier that is uses to dedup transfers
                              // this value is the a timestamp sent from frontend, but in theory can be any unique number
                              uint256 srcPoolId;
                              uint256 dstPoolId;
                              uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination
                              uint256 optionalValue;
                              uint256 destinationGasLimit;
                              bytes32 metadata;
                              bytes destinationPayload;
                          }
                          /**
                           * @notice function to bridge tokens after swap.
                           * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct
                           * @param amount amount of tokens being bridged. this can be ERC20 or native
                           * @param bridgeData encoded data for Stargate-L1-Bridge
                           */
                          function bridgeAfterSwap(
                              uint256 amount,
                              bytes calldata bridgeData
                          ) external payable override {
                              StargateBridgeData memory stargateBridgeData = abi.decode(
                                  bridgeData,
                                  (StargateBridgeData)
                              );
                              if (stargateBridgeData.token == NATIVE_TOKEN_ADDRESS) {
                                  // perform bridging
                                  routerETH.swapETH{value: amount + stargateBridgeData.optionalValue}(
                                      stargateBridgeData.stargateDstChainId,
                                      payable(stargateBridgeData.senderAddress),
                                      abi.encodePacked(stargateBridgeData.receiverAddress),
                                      amount,
                                      stargateBridgeData.minReceivedAmt
                                  );
                              } else {
                                  ERC20(stargateBridgeData.token).safeApprove(
                                      address(router),
                                      amount
                                  );
                                  {
                                      router.swap{value: stargateBridgeData.value}(
                                          stargateBridgeData.stargateDstChainId,
                                          stargateBridgeData.srcPoolId,
                                          stargateBridgeData.dstPoolId,
                                          payable(stargateBridgeData.senderAddress), // default to refund to main contract
                                          amount,
                                          stargateBridgeData.minReceivedAmt,
                                          IBridgeStargate.lzTxObj(
                                              stargateBridgeData.destinationGasLimit,
                                              0, // zero amount since this is a ERC20 bridging
                                              "0x" //empty data since this is for only ERC20
                                          ),
                                          abi.encodePacked(stargateBridgeData.receiverAddress),
                                          stargateBridgeData.destinationPayload
                                      );
                                  }
                              }
                              emit SocketBridge(
                                  amount,
                                  stargateBridgeData.token,
                                  stargateBridgeData.stargateDstChainId,
                                  StargateIdentifier,
                                  msg.sender,
                                  stargateBridgeData.receiverAddress,
                                  stargateBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to bridge tokens after swapping.
                           * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @dev for usage, refer to controller implementations
                           *      encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct
                           * @param swapId routeId for the swapImpl
                           * @param swapData encoded data for swap
                           * @param stargateBridgeData encoded data for StargateBridgeData
                           */
                          function swapAndBridge(
                              uint32 swapId,
                              bytes calldata swapData,
                              StargateBridgeDataNoToken calldata stargateBridgeData
                          ) external payable {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(swapId)
                                  .delegatecall(swapData);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              (uint256 bridgeAmount, address token) = abi.decode(
                                  result,
                                  (uint256, address)
                              );
                              if (token == NATIVE_TOKEN_ADDRESS) {
                                  routerETH.swapETH{
                                      value: bridgeAmount + stargateBridgeData.optionalValue
                                  }(
                                      stargateBridgeData.stargateDstChainId,
                                      payable(stargateBridgeData.senderAddress),
                                      abi.encodePacked(stargateBridgeData.receiverAddress),
                                      bridgeAmount,
                                      stargateBridgeData.minReceivedAmt
                                  );
                              } else {
                                  ERC20(token).safeApprove(address(router), bridgeAmount);
                                  {
                                      router.swap{value: stargateBridgeData.value}(
                                          stargateBridgeData.stargateDstChainId,
                                          stargateBridgeData.srcPoolId,
                                          stargateBridgeData.dstPoolId,
                                          payable(stargateBridgeData.senderAddress), // default to refund to main contract
                                          bridgeAmount,
                                          stargateBridgeData.minReceivedAmt,
                                          IBridgeStargate.lzTxObj(
                                              stargateBridgeData.destinationGasLimit,
                                              0,
                                              "0x"
                                          ),
                                          abi.encodePacked(stargateBridgeData.receiverAddress),
                                          stargateBridgeData.destinationPayload
                                      );
                                  }
                              }
                              emit SocketBridge(
                                  bridgeAmount,
                                  token,
                                  stargateBridgeData.stargateDstChainId,
                                  StargateIdentifier,
                                  msg.sender,
                                  stargateBridgeData.receiverAddress,
                                  stargateBridgeData.metadata
                              );
                          }
                          /**
                           * @notice function to handle ERC20 bridging to receipent via Stargate-L1-Bridge
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param token address of token being bridged
                           * @param senderAddress address of sender
                           * @param receiverAddress address of recipient
                           * @param amount amount of token being bridge
                           * @param value value
                           * @param optionalValue optionalValue
                           * @param stargateBridgeExtraData stargate bridge extradata
                           */
                          function bridgeERC20To(
                              address token,
                              address senderAddress,
                              address receiverAddress,
                              uint256 amount,
                              uint256 value,
                              uint256 optionalValue,
                              StargateBridgeExtraData calldata stargateBridgeExtraData
                          ) external payable {
                              // token address might not be indication thats why passed through extraData
                              if (token == NATIVE_TOKEN_ADDRESS) {
                                  // perform bridging
                                  routerETH.swapETH{value: amount + optionalValue}(
                                      stargateBridgeExtraData.stargateDstChainId,
                                      payable(senderAddress),
                                      abi.encodePacked(receiverAddress),
                                      amount,
                                      stargateBridgeExtraData.minReceivedAmt
                                  );
                              } else {
                                  ERC20 tokenInstance = ERC20(token);
                                  tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);
                                  tokenInstance.safeApprove(address(router), amount);
                                  {
                                      router.swap{value: value}(
                                          stargateBridgeExtraData.stargateDstChainId,
                                          stargateBridgeExtraData.srcPoolId,
                                          stargateBridgeExtraData.dstPoolId,
                                          payable(senderAddress), // default to refund to main contract
                                          amount,
                                          stargateBridgeExtraData.minReceivedAmt,
                                          IBridgeStargate.lzTxObj(
                                              stargateBridgeExtraData.destinationGasLimit,
                                              0, // zero amount since this is a ERC20 bridging
                                              "0x" //empty data since this is for only ERC20
                                          ),
                                          abi.encodePacked(receiverAddress),
                                          stargateBridgeExtraData.destinationPayload
                                      );
                                  }
                              }
                              emit SocketBridge(
                                  amount,
                                  token,
                                  stargateBridgeExtraData.stargateDstChainId,
                                  StargateIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  stargateBridgeExtraData.metadata
                              );
                          }
                          function bridgeNativeTo(
                              address receiverAddress,
                              address senderAddress,
                              uint16 stargateDstChainId,
                              uint256 amount,
                              uint256 minReceivedAmt,
                              uint256 optionalValue,
                              bytes32 metadata
                          ) external payable {
                              // perform bridging
                              routerETH.swapETH{value: amount + optionalValue}(
                                  stargateDstChainId,
                                  payable(senderAddress),
                                  abi.encodePacked(receiverAddress),
                                  amount,
                                  minReceivedAmt
                              );
                              emit SocketBridge(
                                  amount,
                                  NATIVE_TOKEN_ADDRESS,
                                  stargateDstChainId,
                                  StargateIdentifier,
                                  msg.sender,
                                  receiverAddress,
                                  metadata
                              );
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import {ISocketRequest} from "../interfaces/ISocketRequest.sol";
                      import {ISocketRoute} from "../interfaces/ISocketRoute.sol";
                      /// @title BaseController Controller
                      /// @notice Base contract for all controller contracts
                      abstract contract BaseController {
                          /// @notice Address used to identify if it is a native token transfer or not
                          address public immutable NATIVE_TOKEN_ADDRESS =
                              address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
                          /// @notice Address used to identify if it is a Zero address
                          address public immutable NULL_ADDRESS = address(0);
                          /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation
                          bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =
                              bytes4(keccak256("bridgeAfterSwap(uint256,bytes)"));
                          /// @notice immutable variable to store the socketGateway address
                          address public immutable socketGatewayAddress;
                          /// @notice immutable variable with instance of SocketRoute to access route functions
                          ISocketRoute public immutable socketRoute;
                          /**
                           * @notice Construct the base for all controllers.
                           * @param _socketGatewayAddress Socketgateway address, an immutable variable to set.
                           * @notice initialize the immutable variables of SocketRoute, SocketGateway
                           */
                          constructor(address _socketGatewayAddress) {
                              socketGatewayAddress = _socketGatewayAddress;
                              socketRoute = ISocketRoute(_socketGatewayAddress);
                          }
                          /**
                           * @notice Construct the base for all BridgeImplementations.
                           * @param routeId routeId mapped to the routrImplementation
                           * @param data transactionData generated with arguments of bridgeRequest (offchain or by caller)
                           * @return returns the bytes response of the route execution (bridging, refuel or swap executions)
                           */
                          function _executeRoute(
                              uint32 routeId,
                              bytes memory data
                          ) internal returns (bytes memory) {
                              (bool success, bytes memory result) = socketRoute
                                  .getRoute(routeId)
                                  .delegatecall(data);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              return result;
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {BaseController} from "./BaseController.sol";
                      import {ISocketRequest} from "../interfaces/ISocketRequest.sol";
                      /**
                       * @title FeesTaker-Controller Implementation
                       * @notice Controller with composed actions to deduct-fees followed by Refuel, Swap and Bridge
                       *          to be executed Sequentially and this is atomic
                       * @author Socket dot tech.
                       */
                      contract FeesTakerController is BaseController {
                          using SafeTransferLib for ERC20;
                          /// @notice event emitted upon fee-deduction to fees-taker address
                          event SocketFeesDeducted(
                              uint256 fees,
                              address feesToken,
                              address feesTaker
                          );
                          /// @notice Function-selector to invoke deduct-fees and swap token
                          /// @dev This function selector is to be used while building transaction-data
                          bytes4 public immutable FEES_TAKER_SWAP_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256("takeFeesAndSwap((address,address,uint256,uint32,bytes))")
                              );
                          /// @notice Function-selector to invoke deduct-fees and bridge token
                          /// @dev This function selector is to be used while building transaction-data
                          bytes4 public immutable FEES_TAKER_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "takeFeesAndBridge((address,address,uint256,uint32,bytes))"
                                  )
                              );
                          /// @notice Function-selector to invoke deduct-fees and bridge multiple tokens
                          /// @dev This function selector is to be used while building transaction-data
                          bytes4 public immutable FEES_TAKER_MULTI_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "takeFeesAndMultiBridge((address,address,uint256,uint32[],bytes[]))"
                                  )
                              );
                          /// @notice Function-selector to invoke deduct-fees followed by swapping of a token and bridging the swapped bridge
                          /// @dev This function selector is to be used while building transaction-data
                          bytes4 public immutable FEES_TAKER_SWAP_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "takeFeeAndSwapAndBridge((address,address,uint256,uint32,bytes,uint32,bytes))"
                                  )
                              );
                          /// @notice Function-selector to invoke deduct-fees refuel
                          /// @notice followed by swapping of a token and bridging the swapped bridge
                          /// @dev This function selector is to be used while building transaction-data
                          bytes4 public immutable FEES_TAKER_REFUEL_SWAP_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "takeFeeAndRefuelAndSwapAndBridge((address,address,uint256,uint32,bytes,uint32,bytes,uint32,bytes))"
                                  )
                              );
                          /// @notice socketGatewayAddress to be initialised via storage variable BaseController
                          constructor(
                              address _socketGatewayAddress
                          ) BaseController(_socketGatewayAddress) {}
                          /**
                           * @notice function to deduct-fees to fees-taker address on source-chain and swap token
                           * @dev ensure correct function selector is used to generate transaction-data for bridgeRequest
                           * @param ftsRequest feesTakerSwapRequest object generated either off-chain or the calling contract using
                           *                   the function-selector FEES_TAKER_SWAP_FUNCTION_SELECTOR
                           * @return output bytes from the swap operation (last operation in the composed actions)
                           */
                          function takeFeesAndSwap(
                              ISocketRequest.FeesTakerSwapRequest calldata ftsRequest
                          ) external payable returns (bytes memory) {
                              if (ftsRequest.feesToken == NATIVE_TOKEN_ADDRESS) {
                                  //transfer the native amount to the feeTakerAddress
                                  payable(ftsRequest.feesTakerAddress).transfer(
                                      ftsRequest.feesAmount
                                  );
                              } else {
                                  //transfer feesAmount to feesTakerAddress
                                  ERC20(ftsRequest.feesToken).safeTransferFrom(
                                      msg.sender,
                                      ftsRequest.feesTakerAddress,
                                      ftsRequest.feesAmount
                                  );
                              }
                              emit SocketFeesDeducted(
                                  ftsRequest.feesAmount,
                                  ftsRequest.feesTakerAddress,
                                  ftsRequest.feesToken
                              );
                              //call bridge function (executeRoute for the swapRequestData)
                              return _executeRoute(ftsRequest.routeId, ftsRequest.swapRequestData);
                          }
                          /**
                           * @notice function to deduct-fees to fees-taker address on source-chain and bridge amount to destinationChain
                           * @dev ensure correct function selector is used to generate transaction-data for bridgeRequest
                           * @param ftbRequest feesTakerBridgeRequest object generated either off-chain or the calling contract using
                           *                   the function-selector FEES_TAKER_BRIDGE_FUNCTION_SELECTOR
                           * @return output bytes from the bridge operation (last operation in the composed actions)
                           */
                          function takeFeesAndBridge(
                              ISocketRequest.FeesTakerBridgeRequest calldata ftbRequest
                          ) external payable returns (bytes memory) {
                              if (ftbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {
                                  //transfer the native amount to the feeTakerAddress
                                  payable(ftbRequest.feesTakerAddress).transfer(
                                      ftbRequest.feesAmount
                                  );
                              } else {
                                  //transfer feesAmount to feesTakerAddress
                                  ERC20(ftbRequest.feesToken).safeTransferFrom(
                                      msg.sender,
                                      ftbRequest.feesTakerAddress,
                                      ftbRequest.feesAmount
                                  );
                              }
                              emit SocketFeesDeducted(
                                  ftbRequest.feesAmount,
                                  ftbRequest.feesTakerAddress,
                                  ftbRequest.feesToken
                              );
                              //call bridge function (executeRoute for the bridgeData)
                              return _executeRoute(ftbRequest.routeId, ftbRequest.bridgeRequestData);
                          }
                          /**
                           * @notice function to deduct-fees to fees-taker address on source-chain and bridge amount to destinationChain
                           * @notice multiple bridge-requests are to be generated and sequence and number of routeIds should match with the bridgeData array
                           * @dev ensure correct function selector is used to generate transaction-data for bridgeRequest
                           * @param ftmbRequest feesTakerMultiBridgeRequest object generated either off-chain or the calling contract using
                           *                   the function-selector FEES_TAKER_MULTI_BRIDGE_FUNCTION_SELECTOR
                           */
                          function takeFeesAndMultiBridge(
                              ISocketRequest.FeesTakerMultiBridgeRequest calldata ftmbRequest
                          ) external payable {
                              if (ftmbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {
                                  //transfer the native amount to the feeTakerAddress
                                  payable(ftmbRequest.feesTakerAddress).transfer(
                                      ftmbRequest.feesAmount
                                  );
                              } else {
                                  //transfer feesAmount to feesTakerAddress
                                  ERC20(ftmbRequest.feesToken).safeTransferFrom(
                                      msg.sender,
                                      ftmbRequest.feesTakerAddress,
                                      ftmbRequest.feesAmount
                                  );
                              }
                              emit SocketFeesDeducted(
                                  ftmbRequest.feesAmount,
                                  ftmbRequest.feesTakerAddress,
                                  ftmbRequest.feesToken
                              );
                              // multiple bridge-requests are to be generated and sequence and number of routeIds should match with the bridgeData array
                              for (
                                  uint256 index = 0;
                                  index < ftmbRequest.bridgeRouteIds.length;
                                  ++index
                              ) {
                                  //call bridge function (executeRoute for the bridgeData)
                                  _executeRoute(
                                      ftmbRequest.bridgeRouteIds[index],
                                      ftmbRequest.bridgeRequestDataItems[index]
                                  );
                              }
                          }
                          /**
                           * @notice function to deduct-fees to fees-taker address on source-chain followed by swap the amount on sourceChain followed by
                           *         bridging the swapped amount to destinationChain
                           * @dev while generating implData for swap and bridgeRequests, ensure correct function selector is used
                           *      bridge action corresponds to the bridgeAfterSwap function of the bridgeImplementation
                           * @param fsbRequest feesTakerSwapBridgeRequest object generated either off-chain or the calling contract using
                           *                   the function-selector FEES_TAKER_SWAP_BRIDGE_FUNCTION_SELECTOR
                           */
                          function takeFeeAndSwapAndBridge(
                              ISocketRequest.FeesTakerSwapBridgeRequest calldata fsbRequest
                          ) external payable returns (bytes memory) {
                              if (fsbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {
                                  //transfer the native amount to the feeTakerAddress
                                  payable(fsbRequest.feesTakerAddress).transfer(
                                      fsbRequest.feesAmount
                                  );
                              } else {
                                  //transfer feesAmount to feesTakerAddress
                                  ERC20(fsbRequest.feesToken).safeTransferFrom(
                                      msg.sender,
                                      fsbRequest.feesTakerAddress,
                                      fsbRequest.feesAmount
                                  );
                              }
                              emit SocketFeesDeducted(
                                  fsbRequest.feesAmount,
                                  fsbRequest.feesTakerAddress,
                                  fsbRequest.feesToken
                              );
                              // execute swap operation
                              bytes memory swapResponseData = _executeRoute(
                                  fsbRequest.swapRouteId,
                                  fsbRequest.swapData
                              );
                              uint256 swapAmount = abi.decode(swapResponseData, (uint256));
                              // swapped amount is to be bridged to the recipient on destinationChain
                              bytes memory bridgeImpldata = abi.encodeWithSelector(
                                  BRIDGE_AFTER_SWAP_SELECTOR,
                                  swapAmount,
                                  fsbRequest.bridgeData
                              );
                              // execute bridge operation and return the byte-data from response of bridge operation
                              return _executeRoute(fsbRequest.bridgeRouteId, bridgeImpldata);
                          }
                          /**
                           * @notice function to deduct-fees to fees-taker address on source-chain followed by refuel followed by
                           *          swap the amount on sourceChain followed by bridging the swapped amount to destinationChain
                           * @dev while generating implData for refuel, swap and bridge Requests, ensure correct function selector is used
                           *      bridge action corresponds to the bridgeAfterSwap function of the bridgeImplementation
                           * @param frsbRequest feesTakerRefuelSwapBridgeRequest object generated either off-chain or the calling contract using
                           *                   the function-selector FEES_TAKER_REFUEL_SWAP_BRIDGE_FUNCTION_SELECTOR
                           */
                          function takeFeeAndRefuelAndSwapAndBridge(
                              ISocketRequest.FeesTakerRefuelSwapBridgeRequest calldata frsbRequest
                          ) external payable returns (bytes memory) {
                              if (frsbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {
                                  //transfer the native amount to the feeTakerAddress
                                  payable(frsbRequest.feesTakerAddress).transfer(
                                      frsbRequest.feesAmount
                                  );
                              } else {
                                  //transfer feesAmount to feesTakerAddress
                                  ERC20(frsbRequest.feesToken).safeTransferFrom(
                                      msg.sender,
                                      frsbRequest.feesTakerAddress,
                                      frsbRequest.feesAmount
                                  );
                              }
                              emit SocketFeesDeducted(
                                  frsbRequest.feesAmount,
                                  frsbRequest.feesTakerAddress,
                                  frsbRequest.feesToken
                              );
                              // refuel is also done via bridge execution via refuelRouteImplementation identified by refuelRouteId
                              _executeRoute(frsbRequest.refuelRouteId, frsbRequest.refuelData);
                              // execute swap operation
                              bytes memory swapResponseData = _executeRoute(
                                  frsbRequest.swapRouteId,
                                  frsbRequest.swapData
                              );
                              uint256 swapAmount = abi.decode(swapResponseData, (uint256));
                              // swapped amount is to be bridged to the recipient on destinationChain
                              bytes memory bridgeImpldata = abi.encodeWithSelector(
                                  BRIDGE_AFTER_SWAP_SELECTOR,
                                  swapAmount,
                                  frsbRequest.bridgeData
                              );
                              // execute bridge operation and return the byte-data from response of bridge operation
                              return _executeRoute(frsbRequest.bridgeRouteId, bridgeImpldata);
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import {ISocketRequest} from "../interfaces/ISocketRequest.sol";
                      import {ISocketRoute} from "../interfaces/ISocketRoute.sol";
                      import {BaseController} from "./BaseController.sol";
                      /**
                       * @title RefuelSwapAndBridge Controller Implementation
                       * @notice Controller with composed actions for Refuel,Swap and Bridge to be executed Sequentially and this is atomic
                       * @author Socket dot tech.
                       */
                      contract RefuelSwapAndBridgeController is BaseController {
                          /// @notice Function-selector to invoke refuel-swap-bridge function
                          /// @dev This function selector is to be used while buidling transaction-data
                          bytes4 public immutable REFUEL_SWAP_BRIDGE_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256(
                                      "refuelAndSwapAndBridge((uint32,bytes,uint32,bytes,uint32,bytes))"
                                  )
                              );
                          /// @notice socketGatewayAddress to be initialised via storage variable BaseController
                          constructor(
                              address _socketGatewayAddress
                          ) BaseController(_socketGatewayAddress) {}
                          /**
                           * @notice function to handle refuel followed by Swap and Bridge actions
                           * @notice This method is payable because the caller is doing token transfer and briding operation
                           * @param rsbRequest Request with data to execute refuel followed by swap and bridge
                           * @return output data from bridging operation
                           */
                          function refuelAndSwapAndBridge(
                              ISocketRequest.RefuelSwapBridgeRequest calldata rsbRequest
                          ) public payable returns (bytes memory) {
                              _executeRoute(rsbRequest.refuelRouteId, rsbRequest.refuelData);
                              // refuel is also a bridging activity via refuel-route-implementation
                              bytes memory swapResponseData = _executeRoute(
                                  rsbRequest.swapRouteId,
                                  rsbRequest.swapData
                              );
                              uint256 swapAmount = abi.decode(swapResponseData, (uint256));
                              //sequence of arguments for implData: amount, token, data
                              // Bridging the swapAmount received in the preceeding step
                              bytes memory bridgeImpldata = abi.encodeWithSelector(
                                  BRIDGE_AFTER_SWAP_SELECTOR,
                                  swapAmount,
                                  rsbRequest.bridgeData
                              );
                              return _executeRoute(rsbRequest.bridgeRouteId, bridgeImpldata);
                          }
                      }
                      //SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {ISocketGateway} from "../interfaces/ISocketGateway.sol";
                      import {OnlySocketGatewayOwner} from "../errors/SocketErrors.sol";
                      contract DisabledSocketRoute {
                          using SafeTransferLib for ERC20;
                          /// @notice immutable variable to store the socketGateway address
                          address public immutable socketGateway;
                          error RouteDisabled();
                          /**
                           * @notice Construct the base for all BridgeImplementations.
                           * @param _socketGateway Socketgateway address, an immutable variable to set.
                           */
                          constructor(address _socketGateway) {
                              socketGateway = _socketGateway;
                          }
                          /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used
                          modifier isSocketGatewayOwner() {
                              if (msg.sender != ISocketGateway(socketGateway).owner()) {
                                  revert OnlySocketGatewayOwner();
                              }
                              _;
                          }
                          /**
                           * @notice function to rescue the ERC20 tokens in the bridge Implementation contract
                           * @notice this is a function restricted to Owner of SocketGateway only
                           * @param token address of ERC20 token being rescued
                           * @param userAddress receipient address to which ERC20 tokens will be rescued to
                           * @param amount amount of ERC20 tokens being rescued
                           */
                          function rescueFunds(
                              address token,
                              address userAddress,
                              uint256 amount
                          ) external isSocketGatewayOwner {
                              ERC20(token).safeTransfer(userAddress, amount);
                          }
                          /**
                           * @notice function to rescue the native-balance in the bridge Implementation contract
                           * @notice this is a function restricted to Owner of SocketGateway only
                           * @param userAddress receipient address to which native-balance will be rescued to
                           * @param amount amount of native balance tokens being rescued
                           */
                          function rescueEther(
                              address payable userAddress,
                              uint256 amount
                          ) external isSocketGatewayOwner {
                              userAddress.transfer(amount);
                          }
                          /**
                           * @notice Handle route function calls gracefully.
                           */
                          fallback() external payable {
                              revert RouteDisabled();
                          }
                          /**
                           * @notice Support receiving ether to handle refunds etc.
                           */
                          receive() external payable {}
                      }
                      //SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import "../utils/Ownable.sol";
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {ISocketBridgeBase} from "../interfaces/ISocketBridgeBase.sol";
                      /**
                       * @dev In the constructor, set up the initialization code for socket
                       * contracts as well as the keccak256 hash of the given initialization code.
                       * that will be used to deploy any transient contracts, which will deploy any
                       * socket contracts that require the use of a constructor.
                       *
                       * Socket contract initialization code (29 bytes):
                       *
                       *       0x5860208158601c335a63aaf10f428752fa158151803b80938091923cf3
                       *
                       * Description:
                       *
                       * pc|op|name         | [stack]                                | <memory>
                       *
                       * ** set the first stack item to zero - used later **
                       * 00 58 getpc          [0]                                       <>
                       *
                       * ** set second stack item to 32, length of word returned from staticcall **
                       * 01 60 push1
                       * 02 20 outsize        [0, 32]                                   <>
                       *
                       * ** set third stack item to 0, position of word returned from staticcall **
                       * 03 81 dup2           [0, 32, 0]                                <>
                       *
                       * ** set fourth stack item to 4, length of selector given to staticcall **
                       * 04 58 getpc          [0, 32, 0, 4]                             <>
                       *
                       * ** set fifth stack item to 28, position of selector given to staticcall **
                       * 05 60 push1
                       * 06 1c inpos          [0, 32, 0, 4, 28]                         <>
                       *
                       * ** set the sixth stack item to msg.sender, target address for staticcall **
                       * 07 33 caller         [0, 32, 0, 4, 28, caller]                 <>
                       *
                       * ** set the seventh stack item to msg.gas, gas to forward for staticcall **
                       * 08 5a gas            [0, 32, 0, 4, 28, caller, gas]            <>
                       *
                       * ** set the eighth stack item to selector, "what" to store via mstore **
                       * 09 63 push4
                       * 10 aaf10f42 selector [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42]    <>
                       *
                       * ** set the ninth stack item to 0, "where" to store via mstore ***
                       * 11 87 dup8           [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42, 0] <>
                       *
                       * ** call mstore, consume 8 and 9 from the stack, place selector in memory **
                       * 12 52 mstore         [0, 32, 0, 4, 0, caller, gas]             <0xaaf10f42>
                       *
                       * ** call staticcall, consume items 2 through 7, place address in memory **
                       * 13 fa staticcall     [0, 1 (if successful)]                    <address>
                       *
                       * ** flip success bit in second stack item to set to 0 **
                       * 14 15 iszero         [0, 0]                                    <address>
                       *
                       * ** push a third 0 to the stack, position of address in memory **
                       * 15 81 dup2           [0, 0, 0]                                 <address>
                       *
                       * ** place address from position in memory onto third stack item **
                       * 16 51 mload          [0, 0, address]                           <>
                       *
                       * ** place address to fourth stack item for extcodesize to consume **
                       * 17 80 dup1           [0, 0, address, address]                  <>
                       *
                       * ** get extcodesize on fourth stack item for extcodecopy **
                       * 18 3b extcodesize    [0, 0, address, size]                     <>
                       *
                       * ** dup and swap size for use by return at end of init code **
                       * 19 80 dup1           [0, 0, address, size, size]               <>
                       * 20 93 swap4          [size, 0, address, size, 0]               <>
                       *
                       * ** push code position 0 to stack and reorder stack items for extcodecopy **
                       * 21 80 dup1           [size, 0, address, size, 0, 0]            <>
                       * 22 91 swap2          [size, 0, address, 0, 0, size]            <>
                       * 23 92 swap3          [size, 0, size, 0, 0, address]            <>
                       *
                       * ** call extcodecopy, consume four items, clone runtime code to memory **
                       * 24 3c extcodecopy    [size, 0]                                 <code>
                       *
                       * ** return to deploy final code in memory **
                       * 25 f3 return         []                                        *deployed!*
                       */
                      contract SocketDeployFactory is Ownable {
                          using SafeTransferLib for ERC20;
                          address public immutable disabledRouteAddress;
                          mapping(address => address) _implementations;
                          mapping(uint256 => bool) isDisabled;
                          mapping(uint256 => bool) isRouteDeployed;
                          mapping(address => bool) canDisableRoute;
                          event Deployed(address _addr);
                          event DisabledRoute(address _addr);
                          event Destroyed(address _addr);
                          error ContractAlreadyDeployed();
                          error NothingToDestroy();
                          error AlreadyDisabled();
                          error CannotBeDisabled();
                          error OnlyDisabler();
                          constructor(address _owner, address disabledRoute) Ownable(_owner) {
                              disabledRouteAddress = disabledRoute;
                              canDisableRoute[_owner] = true;
                          }
                          modifier onlyDisabler() {
                              if (!canDisableRoute[msg.sender]) {
                                  revert OnlyDisabler();
                              }
                              _;
                          }
                          function addDisablerAddress(address disabler) external onlyOwner {
                              canDisableRoute[disabler] = true;
                          }
                          function removeDisablerAddress(address disabler) external onlyOwner {
                              canDisableRoute[disabler] = false;
                          }
                          /**
                           * @notice Deploys a route contract at predetermined location
                           * @notice Caller must first deploy the route contract at another location and pass its address as implementation.
                           * @param routeId route identifier
                           * @param implementationContract address of deployed route contract. Its byte code will be copied to predetermined location.
                           */
                          function deploy(
                              uint256 routeId,
                              address implementationContract
                          ) external onlyOwner returns (address) {
                              // assign the initialization code for the socket contract.
                              bytes memory initCode = (
                                  hex"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3"
                              );
                              // determine the address of the socket contract.
                              address routeContractAddress = _getContractAddress(routeId);
                              if (isRouteDeployed[routeId]) {
                                  revert ContractAlreadyDeployed();
                              }
                              isRouteDeployed[routeId] = true;
                              //first we deploy the code we want to deploy on a separate address
                              // store the implementation to be retrieved by the socket contract.
                              _implementations[routeContractAddress] = implementationContract;
                              address addr;
                              assembly {
                                  let encoded_data := add(0x20, initCode) // load initialization code.
                                  let encoded_size := mload(initCode) // load init code's length.
                                  addr := create2(0, encoded_data, encoded_size, routeId) // routeId is used as salt
                              }
                              require(
                                  addr == routeContractAddress,
                                  "Failed to deploy the new socket contract."
                              );
                              emit Deployed(addr);
                              return addr;
                          }
                          /**
                           * @notice Destroy the route deployed at a location.
                           * @param routeId route identifier to be destroyed.
                           */
                          function destroy(uint256 routeId) external onlyDisabler {
                              // determine the address of the socket contract.
                              _destroy(routeId);
                          }
                          /**
                           * @notice Deploy a disabled contract at destroyed route to handle it gracefully.
                           * @param routeId route identifier to be disabled.
                           */
                          function disableRoute(
                              uint256 routeId
                          ) external onlyDisabler returns (address) {
                              return _disableRoute(routeId);
                          }
                          /**
                           * @notice Destroy a list of routeIds
                           * @param routeIds array of routeIds to be destroyed.
                           */
                          function multiDestroy(uint256[] calldata routeIds) external onlyDisabler {
                              for (uint32 index = 0; index < routeIds.length; ) {
                                  _destroy(routeIds[index]);
                                  unchecked {
                                      ++index;
                                  }
                              }
                          }
                          /**
                           * @notice Deploy a disabled contract at list of routeIds.
                           * @param routeIds array of routeIds to be disabled.
                           */
                          function multiDisableRoute(
                              uint256[] calldata routeIds
                          ) external onlyDisabler {
                              for (uint32 index = 0; index < routeIds.length; ) {
                                  _disableRoute(routeIds[index]);
                                  unchecked {
                                      ++index;
                                  }
                              }
                          }
                          /**
                           * @dev External view function for calculating a socket contract address
                           * given a particular routeId.
                           */
                          function getContractAddress(
                              uint256 routeId
                          ) external view returns (address) {
                              // determine the address of the socket contract.
                              return _getContractAddress(routeId);
                          }
                          //those two functions are getting called by the socket Contract
                          function getImplementation()
                              external
                              view
                              returns (address implementation)
                          {
                              return _implementations[msg.sender];
                          }
                          function _disableRoute(uint256 routeId) internal returns (address) {
                              // assign the initialization code for the socket contract.
                              bytes memory initCode = (
                                  hex"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3"
                              );
                              // determine the address of the socket contract.
                              address routeContractAddress = _getContractAddress(routeId);
                              if (!isRouteDeployed[routeId]) {
                                  revert CannotBeDisabled();
                              }
                              if (isDisabled[routeId]) {
                                  revert AlreadyDisabled();
                              }
                              isDisabled[routeId] = true;
                              //first we deploy the code we want to deploy on a separate address
                              // store the implementation to be retrieved by the socket contract.
                              _implementations[routeContractAddress] = disabledRouteAddress;
                              address addr;
                              assembly {
                                  let encoded_data := add(0x20, initCode) // load initialization code.
                                  let encoded_size := mload(initCode) // load init code's length.
                                  addr := create2(0, encoded_data, encoded_size, routeId) // routeId is used as salt.
                              }
                              require(
                                  addr == routeContractAddress,
                                  "Failed to deploy the new socket contract."
                              );
                              emit Deployed(addr);
                              return addr;
                          }
                          function _destroy(uint256 routeId) internal {
                              // determine the address of the socket contract.
                              address routeContractAddress = _getContractAddress(routeId);
                              if (!isRouteDeployed[routeId]) {
                                  revert NothingToDestroy();
                              }
                              ISocketBridgeBase(routeContractAddress).killme();
                              emit Destroyed(routeContractAddress);
                          }
                          /**
                           * @dev Internal view function for calculating a socket contract address
                           * given a particular routeId.
                           */
                          function _getContractAddress(
                              uint256 routeId
                          ) internal view returns (address) {
                              // determine the address of the socket contract.
                              bytes memory initCode = (
                                  hex"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3"
                              );
                              return
                                  address(
                                      uint160( // downcast to match the address type.
                                          uint256( // convert to uint to truncate upper digits.
                                              keccak256( // compute the CREATE2 hash using 4 inputs.
                                                  abi.encodePacked( // pack all inputs to the hash together.
                                                      hex"ff", // start with 0xff to distinguish from RLP.
                                                      address(this), // this contract will be the caller.
                                                      routeId, // the routeId is used as salt.
                                                      keccak256(abi.encodePacked(initCode)) // the init code hash.
                                                  )
                                              )
                                          )
                                      )
                                  );
                          }
                          /**
                           * @notice Rescues the ERC20 token to an address
                                     this is a restricted function to be called by only socketGatewayOwner
                           * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address
                           * @param token address of the ERC20 token being rescued
                           * @param userAddress address to which ERC20 is to be rescued
                           * @param amount amount of ERC20 tokens being rescued
                           */
                          function rescueFunds(
                              address token,
                              address userAddress,
                              uint256 amount
                          ) external onlyOwner {
                              ERC20(token).safeTransfer(userAddress, amount);
                          }
                          /**
                           * @notice Rescues the native balance to an address
                                     this is a restricted function to be called by only socketGatewayOwner
                           * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address
                           * @param userAddress address to which native-balance is to be rescued
                           * @param amount amount of native-balance being rescued
                           */
                          function rescueEther(
                              address payable userAddress,
                              uint256 amount
                          ) external onlyOwner {
                              userAddress.transfer(amount);
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      error CelerRefundNotReady();
                      error OnlySocketDeployer();
                      error OnlySocketGatewayOwner();
                      error OnlySocketGateway();
                      error OnlyOwner();
                      error OnlyNominee();
                      error TransferIdExists();
                      error TransferIdDoesnotExist();
                      error Address0Provided();
                      error SwapFailed();
                      error UnsupportedInterfaceId();
                      error InvalidCelerRefund();
                      error CelerAlreadyRefunded();
                      error IncorrectBridgeRatios();
                      error ZeroAddressNotAllowed();
                      error ArrayLengthMismatch();
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      interface ISocketBridgeBase {
                          function killme() external;
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      /**
                       * @title ISocketController
                       * @notice Interface for SocketController functions.
                       * @dev functions can be added here for invocation from external contracts or off-chain
                       *      only restriction is that this should have functions to manage controllers
                       * @author Socket dot tech.
                       */
                      interface ISocketController {
                          /**
                           * @notice Add controller to the socketGateway
                                     This is a restricted function to be called by only socketGatewayOwner
                           * @dev ensure controllerAddress is a verified controller implementation address
                           * @param _controllerAddress The address of controller implementation contract deployed
                           * @return Id of the controller added to the controllers-mapping in socketGateway storage
                           */
                          function addController(
                              address _controllerAddress
                          ) external returns (uint32);
                          /**
                           * @notice disable controller by setting ZeroAddress to the entry in controllers-mapping
                                     identified by controllerId as key.
                                     This is a restricted function to be called by only socketGatewayOwner
                           * @param _controllerId The Id of controller-implementation in the controllers mapping
                           */
                          function disableController(uint32 _controllerId) external;
                          /**
                           * @notice Get controllerImplementation address mapped to the controllerId
                           * @param _controllerId controllerId is the key in the mapping for controllers
                           * @return controller-implementation address
                           */
                          function getController(uint32 _controllerId) external returns (address);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      /**
                       * @title ISocketGateway
                       * @notice Interface for SocketGateway functions.
                       * @dev functions can be added here for invocation from external contracts or off-chain
                       * @author Socket dot tech.
                       */
                      interface ISocketGateway {
                          /**
                           * @notice Request-struct for controllerRequests
                           * @dev ensure the value for data is generated using the function-selectors defined in the controllerImplementation contracts
                           */
                          struct SocketControllerRequest {
                              // controllerId is the id mapped to the controllerAddress
                              uint32 controllerId;
                              // transactionImplData generated off-chain or by caller using function-selector of the controllerContract
                              bytes data;
                          }
                          // @notice view to get owner-address
                          function owner() external view returns (address);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      /**
                       * @title ISocketRoute
                       * @notice Interface with Request DataStructures to invoke controller functions.
                       * @author Socket dot tech.
                       */
                      interface ISocketRequest {
                          struct SwapMultiBridgeRequest {
                              uint32 swapRouteId;
                              bytes swapImplData;
                              uint32[] bridgeRouteIds;
                              bytes[] bridgeImplDataItems;
                              uint256[] bridgeRatios;
                              bytes[] eventDataItems;
                          }
                          // Datastructure for Refuel-Swap-Bridge function
                          struct RefuelSwapBridgeRequest {
                              uint32 refuelRouteId;
                              bytes refuelData;
                              uint32 swapRouteId;
                              bytes swapData;
                              uint32 bridgeRouteId;
                              bytes bridgeData;
                          }
                          // Datastructure for DeductFees-Swap function
                          struct FeesTakerSwapRequest {
                              address feesTakerAddress;
                              address feesToken;
                              uint256 feesAmount;
                              uint32 routeId;
                              bytes swapRequestData;
                          }
                          // Datastructure for DeductFees-Bridge function
                          struct FeesTakerBridgeRequest {
                              address feesTakerAddress;
                              address feesToken;
                              uint256 feesAmount;
                              uint32 routeId;
                              bytes bridgeRequestData;
                          }
                          // Datastructure for DeductFees-MultiBridge function
                          struct FeesTakerMultiBridgeRequest {
                              address feesTakerAddress;
                              address feesToken;
                              uint256 feesAmount;
                              uint32[] bridgeRouteIds;
                              bytes[] bridgeRequestDataItems;
                          }
                          // Datastructure for DeductFees-Swap-Bridge function
                          struct FeesTakerSwapBridgeRequest {
                              address feesTakerAddress;
                              address feesToken;
                              uint256 feesAmount;
                              uint32 swapRouteId;
                              bytes swapData;
                              uint32 bridgeRouteId;
                              bytes bridgeData;
                          }
                          // Datastructure for DeductFees-Refuel-Swap-Bridge function
                          struct FeesTakerRefuelSwapBridgeRequest {
                              address feesTakerAddress;
                              address feesToken;
                              uint256 feesAmount;
                              uint32 refuelRouteId;
                              bytes refuelData;
                              uint32 swapRouteId;
                              bytes swapData;
                              uint32 bridgeRouteId;
                              bytes bridgeData;
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      /**
                       * @title ISocketRoute
                       * @notice Interface for routeManagement functions in SocketGateway.
                       * @author Socket dot tech.
                       */
                      interface ISocketRoute {
                          /**
                           * @notice Add route to the socketGateway
                                     This is a restricted function to be called by only socketGatewayOwner
                           * @dev ensure routeAddress is a verified bridge or middleware implementation address
                           * @param routeAddress The address of bridge or middleware implementation contract deployed
                           * @return Id of the route added to the routes-mapping in socketGateway storage
                           */
                          function addRoute(address routeAddress) external returns (uint256);
                          /**
                           * @notice disable a route by setting ZeroAddress to the entry in routes-mapping
                                     identified by routeId as key.
                                     This is a restricted function to be called by only socketGatewayOwner
                           * @param routeId The Id of route-implementation in the routes mapping
                           */
                          function disableRoute(uint32 routeId) external;
                          /**
                           * @notice Get routeImplementation address mapped to the routeId
                           * @param routeId routeId is the key in the mapping for routes
                           * @return route-implementation address
                           */
                          function getRoute(uint32 routeId) external view returns (address);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      // Functions taken out from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol
                      library LibBytes {
                          // solhint-disable no-inline-assembly
                          // LibBytes specific errors
                          error SliceOverflow();
                          error SliceOutOfBounds();
                          error AddressOutOfBounds();
                          error UintOutOfBounds();
                          // -------------------------
                          function concat(
                              bytes memory _preBytes,
                              bytes memory _postBytes
                          ) internal pure returns (bytes memory) {
                              bytes memory tempBytes;
                              assembly {
                                  // Get a location of some free memory and store it in tempBytes as
                                  // Solidity does for memory variables.
                                  tempBytes := mload(0x40)
                                  // Store the length of the first bytes array at the beginning of
                                  // the memory for tempBytes.
                                  let length := mload(_preBytes)
                                  mstore(tempBytes, length)
                                  // Maintain a memory counter for the current write location in the
                                  // temp bytes array by adding the 32 bytes for the array length to
                                  // the starting location.
                                  let mc := add(tempBytes, 0x20)
                                  // Stop copying when the memory counter reaches the length of the
                                  // first bytes array.
                                  let end := add(mc, length)
                                  for {
                                      // Initialize a copy counter to the start of the _preBytes data,
                                      // 32 bytes into its memory.
                                      let cc := add(_preBytes, 0x20)
                                  } lt(mc, end) {
                                      // Increase both counters by 32 bytes each iteration.
                                      mc := add(mc, 0x20)
                                      cc := add(cc, 0x20)
                                  } {
                                      // Write the _preBytes data into the tempBytes memory 32 bytes
                                      // at a time.
                                      mstore(mc, mload(cc))
                                  }
                                  // Add the length of _postBytes to the current length of tempBytes
                                  // and store it as the new length in the first 32 bytes of the
                                  // tempBytes memory.
                                  length := mload(_postBytes)
                                  mstore(tempBytes, add(length, mload(tempBytes)))
                                  // Move the memory counter back from a multiple of 0x20 to the
                                  // actual end of the _preBytes data.
                                  mc := end
                                  // Stop copying when the memory counter reaches the new combined
                                  // length of the arrays.
                                  end := add(mc, length)
                                  for {
                                      let cc := add(_postBytes, 0x20)
                                  } lt(mc, end) {
                                      mc := add(mc, 0x20)
                                      cc := add(cc, 0x20)
                                  } {
                                      mstore(mc, mload(cc))
                                  }
                                  // Update the free-memory pointer by padding our last write location
                                  // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
                                  // next 32 byte block, then round down to the nearest multiple of
                                  // 32. If the sum of the length of the two arrays is zero then add
                                  // one before rounding down to leave a blank 32 bytes (the length block with 0).
                                  mstore(
                                      0x40,
                                      and(
                                          add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                                          not(31) // Round down to the nearest 32 bytes.
                                      )
                                  )
                              }
                              return tempBytes;
                          }
                          function slice(
                              bytes memory _bytes,
                              uint256 _start,
                              uint256 _length
                          ) internal pure returns (bytes memory) {
                              if (_length + 31 < _length) {
                                  revert SliceOverflow();
                              }
                              if (_bytes.length < _start + _length) {
                                  revert SliceOutOfBounds();
                              }
                              bytes memory tempBytes;
                              assembly {
                                  switch iszero(_length)
                                  case 0 {
                                      // Get a location of some free memory and store it in tempBytes as
                                      // Solidity does for memory variables.
                                      tempBytes := mload(0x40)
                                      // The first word of the slice result is potentially a partial
                                      // word read from the original array. To read it, we calculate
                                      // the length of that partial word and start copying that many
                                      // bytes into the array. The first word we copy will start with
                                      // data we don't care about, but the last `lengthmod` bytes will
                                      // land at the beginning of the contents of the new array. When
                                      // we're done copying, we overwrite the full first word with
                                      // the actual length of the slice.
                                      let lengthmod := and(_length, 31)
                                      // The multiplication in the next line is necessary
                                      // because when slicing multiples of 32 bytes (lengthmod == 0)
                                      // the following copy loop was copying the origin's length
                                      // and then ending prematurely not copying everything it should.
                                      let mc := add(
                                          add(tempBytes, lengthmod),
                                          mul(0x20, iszero(lengthmod))
                                      )
                                      let end := add(mc, _length)
                                      for {
                                          // The multiplication in the next line has the same exact purpose
                                          // as the one above.
                                          let cc := add(
                                              add(
                                                  add(_bytes, lengthmod),
                                                  mul(0x20, iszero(lengthmod))
                                              ),
                                              _start
                                          )
                                      } lt(mc, end) {
                                          mc := add(mc, 0x20)
                                          cc := add(cc, 0x20)
                                      } {
                                          mstore(mc, mload(cc))
                                      }
                                      mstore(tempBytes, _length)
                                      //update free-memory pointer
                                      //allocating the array padded to 32 bytes like the compiler does now
                                      mstore(0x40, and(add(mc, 31), not(31)))
                                  }
                                  //if we want a zero-length slice let's just return a zero-length array
                                  default {
                                      tempBytes := mload(0x40)
                                      //zero out the 32 bytes slice we are about to return
                                      //we need to do it because Solidity does not garbage collect
                                      mstore(tempBytes, 0)
                                      mstore(0x40, add(tempBytes, 0x20))
                                  }
                              }
                              return tempBytes;
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import "./LibBytes.sol";
                      /// @title LibUtil library
                      /// @notice library with helper functions to operate on bytes-data and addresses
                      /// @author socket dot tech
                      library LibUtil {
                          /// @notice LibBytes library to handle operations on bytes
                          using LibBytes for bytes;
                          /// @notice function to extract revertMessage from bytes data
                          /// @dev use the revertMessage and then further revert with a custom revert and message
                          /// @param _res bytes data received from the transaction call
                          function getRevertMsg(
                              bytes memory _res
                          ) internal pure returns (string memory) {
                              // If the _res length is less than 68, then the transaction failed silently (without a revert message)
                              if (_res.length < 68) {
                                  return "Transaction reverted silently";
                              }
                              bytes memory revertData = _res.slice(4, _res.length - 4); // Remove the selector which is the first 4 bytes
                              return abi.decode(revertData, (string)); // All that remains is the revert string
                          }
                      }
                      // SPDX-License-Identifier: GPL-3.0-only
                      pragma solidity ^0.8.4;
                      // runtime proto sol library
                      library Pb {
                          enum WireType {
                              Varint,
                              Fixed64,
                              LengthDelim,
                              StartGroup,
                              EndGroup,
                              Fixed32
                          }
                          struct Buffer {
                              uint256 idx; // the start index of next read. when idx=b.length, we're done
                              bytes b; // hold serialized proto msg, readonly
                          }
                          // create a new in-memory Buffer object from raw msg bytes
                          function fromBytes(
                              bytes memory raw
                          ) internal pure returns (Buffer memory buf) {
                              buf.b = raw;
                              buf.idx = 0;
                          }
                          // whether there are unread bytes
                          function hasMore(Buffer memory buf) internal pure returns (bool) {
                              return buf.idx < buf.b.length;
                          }
                          // decode current field number and wiretype
                          function decKey(
                              Buffer memory buf
                          ) internal pure returns (uint256 tag, WireType wiretype) {
                              uint256 v = decVarint(buf);
                              tag = v / 8;
                              wiretype = WireType(v & 7);
                          }
                          // read varint from current buf idx, move buf.idx to next read, return the int value
                          function decVarint(Buffer memory buf) internal pure returns (uint256 v) {
                              bytes10 tmp; // proto int is at most 10 bytes (7 bits can be used per byte)
                              bytes memory bb = buf.b; // get buf.b mem addr to use in assembly
                              v = buf.idx; // use v to save one additional uint variable
                              assembly {
                                  tmp := mload(add(add(bb, 32), v)) // load 10 bytes from buf.b[buf.idx] to tmp
                              }
                              uint256 b; // store current byte content
                              v = 0; // reset to 0 for return value
                              for (uint256 i = 0; i < 10; i++) {
                                  assembly {
                                      b := byte(i, tmp) // don't use tmp[i] because it does bound check and costs extra
                                  }
                                  v |= (b & 0x7F) << (i * 7);
                                  if (b & 0x80 == 0) {
                                      buf.idx += i + 1;
                                      return v;
                                  }
                              }
                              revert(); // i=10, invalid varint stream
                          }
                          // read length delimited field and return bytes
                          function decBytes(
                              Buffer memory buf
                          ) internal pure returns (bytes memory b) {
                              uint256 len = decVarint(buf);
                              uint256 end = buf.idx + len;
                              require(end <= buf.b.length); // avoid overflow
                              b = new bytes(len);
                              bytes memory bufB = buf.b; // get buf.b mem addr to use in assembly
                              uint256 bStart;
                              uint256 bufBStart = buf.idx;
                              assembly {
                                  bStart := add(b, 32)
                                  bufBStart := add(add(bufB, 32), bufBStart)
                              }
                              for (uint256 i = 0; i < len; i += 32) {
                                  assembly {
                                      mstore(add(bStart, i), mload(add(bufBStart, i)))
                                  }
                              }
                              buf.idx = end;
                          }
                          // move idx pass current value field, to beginning of next tag or msg end
                          function skipValue(Buffer memory buf, WireType wire) internal pure {
                              if (wire == WireType.Varint) {
                                  decVarint(buf);
                              } else if (wire == WireType.LengthDelim) {
                                  uint256 len = decVarint(buf);
                                  buf.idx += len; // skip len bytes value data
                                  require(buf.idx <= buf.b.length); // avoid overflow
                              } else {
                                  revert();
                              } // unsupported wiretype
                          }
                          function _uint256(bytes memory b) internal pure returns (uint256 v) {
                              require(b.length <= 32); // b's length must be smaller than or equal to 32
                              assembly {
                                  v := mload(add(b, 32))
                              } // load all 32bytes to v
                              v = v >> (8 * (32 - b.length)); // only first b.length is valid
                          }
                          function _address(bytes memory b) internal pure returns (address v) {
                              v = _addressPayable(b);
                          }
                          function _addressPayable(
                              bytes memory b
                          ) internal pure returns (address payable v) {
                              require(b.length == 20);
                              //load 32bytes then shift right 12 bytes
                              assembly {
                                  v := div(mload(add(b, 32)), 0x1000000000000000000000000)
                              }
                          }
                          function _bytes32(bytes memory b) internal pure returns (bytes32 v) {
                              require(b.length == 32);
                              assembly {
                                  v := mload(add(b, 32))
                              }
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      pragma experimental ABIEncoderV2;
                      import "./utils/Ownable.sol";
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {LibUtil} from "./libraries/LibUtil.sol";
                      import "./libraries/LibBytes.sol";
                      import {ISocketRoute} from "./interfaces/ISocketRoute.sol";
                      import {ISocketRequest} from "./interfaces/ISocketRequest.sol";
                      import {ISocketGateway} from "./interfaces/ISocketGateway.sol";
                      import {IncorrectBridgeRatios, ZeroAddressNotAllowed, ArrayLengthMismatch} from "./errors/SocketErrors.sol";
                      /// @title SocketGatewayContract
                      /// @notice Socketgateway is a contract with entrypoint functions for all interactions with socket liquidity layer
                      /// @author Socket Team
                      contract SocketGatewayTemplate is Ownable {
                          using LibBytes for bytes;
                          using LibBytes for bytes4;
                          using SafeTransferLib for ERC20;
                          /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation
                          bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =
                              bytes4(keccak256("bridgeAfterSwap(uint256,bytes)"));
                          /// @notice storage variable to keep track of total number of routes registered in socketgateway
                          uint32 public routesCount = 385;
                          /// @notice storage variable to keep track of total number of controllers registered in socketgateway
                          uint32 public controllerCount;
                          address public immutable disabledRouteAddress;
                          uint256 public constant CENT_PERCENT = 100e18;
                          /// @notice storage mapping for route implementation addresses
                          mapping(uint32 => address) public routes;
                          /// storage mapping for controller implemenation addresses
                          mapping(uint32 => address) public controllers;
                          // Events ------------------------------------------------------------------------------------------------------->
                          /// @notice Event emitted when a router is added to socketgateway
                          event NewRouteAdded(uint32 indexed routeId, address indexed route);
                          /// @notice Event emitted when a route is disabled
                          event RouteDisabled(uint32 indexed routeId);
                          /// @notice Event emitted when ownership transfer is requested by socket-gateway-owner
                          event OwnershipTransferRequested(
                              address indexed _from,
                              address indexed _to
                          );
                          /// @notice Event emitted when a controller is added to socketgateway
                          event ControllerAdded(
                              uint32 indexed controllerId,
                              address indexed controllerAddress
                          );
                          /// @notice Event emitted when a controller is disabled
                          event ControllerDisabled(uint32 indexed controllerId);
                          constructor(address _owner, address _disabledRoute) Ownable(_owner) {
                              disabledRouteAddress = _disabledRoute;
                          }
                          // Able to receive ether
                          // solhint-disable-next-line no-empty-blocks
                          receive() external payable {}
                          /*******************************************
                           *          EXTERNAL AND PUBLIC FUNCTIONS  *
                           *******************************************/
                          /**
                           * @notice executes functions in the routes identified using routeId and functionSelectorData
                           * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped
                           * @dev ensure the data in routeData to be built using the function-selector defined as a
                           *         constant in the route implementation contract
                           * @param routeId route identifier
                           * @param routeData functionSelectorData generated using the function-selector defined in the route Implementation
                           */
                          function executeRoute(
                              uint32 routeId,
                              bytes calldata routeData
                          ) external payable returns (bytes memory) {
                              (bool success, bytes memory result) = addressAt(routeId).delegatecall(
                                  routeData
                              );
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              return result;
                          }
                          /**
                           * @notice swaps a token on sourceChain and split it across multiple bridge-recipients
                           * @notice The caller must first approve this contract to spend amount of ERC20-Token being swapped
                           * @dev ensure the swap-data and bridge-data is generated using the function-selector defined as a constant in the implementation address
                           * @param swapMultiBridgeRequest request
                           */
                          function swapAndMultiBridge(
                              ISocketRequest.SwapMultiBridgeRequest calldata swapMultiBridgeRequest
                          ) external payable {
                              uint256 requestLength = swapMultiBridgeRequest.bridgeRouteIds.length;
                              if (
                                  requestLength != swapMultiBridgeRequest.bridgeImplDataItems.length
                              ) {
                                  revert ArrayLengthMismatch();
                              }
                              uint256 ratioAggregate;
                              for (uint256 index = 0; index < requestLength; ) {
                                  ratioAggregate += swapMultiBridgeRequest.bridgeRatios[index];
                              }
                              if (ratioAggregate != CENT_PERCENT) {
                                  revert IncorrectBridgeRatios();
                              }
                              (bool swapSuccess, bytes memory swapResult) = addressAt(
                                  swapMultiBridgeRequest.swapRouteId
                              ).delegatecall(swapMultiBridgeRequest.swapImplData);
                              if (!swapSuccess) {
                                  assembly {
                                      revert(add(swapResult, 32), mload(swapResult))
                                  }
                              }
                              uint256 amountReceivedFromSwap = abi.decode(swapResult, (uint256));
                              uint256 bridgedAmount;
                              for (uint256 index = 0; index < requestLength; ) {
                                  uint256 bridgingAmount;
                                  // if it is the last bridge request, bridge the remaining amount
                                  if (index == requestLength - 1) {
                                      bridgingAmount = amountReceivedFromSwap - bridgedAmount;
                                  } else {
                                      // bridging amount is the multiplication of bridgeRatio and amountReceivedFromSwap
                                      bridgingAmount =
                                          (amountReceivedFromSwap *
                                              swapMultiBridgeRequest.bridgeRatios[index]) /
                                          (CENT_PERCENT);
                                  }
                                  // update the bridged amount, this would be used for computation for last bridgeRequest
                                  bridgedAmount += bridgingAmount;
                                  bytes memory bridgeImpldata = abi.encodeWithSelector(
                                      BRIDGE_AFTER_SWAP_SELECTOR,
                                      bridgingAmount,
                                      swapMultiBridgeRequest.bridgeImplDataItems[index]
                                  );
                                  (bool bridgeSuccess, bytes memory bridgeResult) = addressAt(
                                      swapMultiBridgeRequest.bridgeRouteIds[index]
                                  ).delegatecall(bridgeImpldata);
                                  if (!bridgeSuccess) {
                                      assembly {
                                          revert(add(bridgeResult, 32), mload(bridgeResult))
                                      }
                                  }
                                  unchecked {
                                      ++index;
                                  }
                              }
                          }
                          /**
                           * @notice sequentially executes functions in the routes identified using routeId and functionSelectorData
                           * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped
                           * @dev ensure the data in each dataItem to be built using the function-selector defined as a
                           *         constant in the route implementation contract
                           * @param routeIds a list of route identifiers
                           * @param dataItems a list of functionSelectorData generated using the function-selector defined in the route Implementation
                           */
                          function executeRoutes(
                              uint32[] calldata routeIds,
                              bytes[] calldata dataItems
                          ) external payable {
                              uint256 routeIdslength = routeIds.length;
                              if (routeIdslength != dataItems.length) revert ArrayLengthMismatch();
                              for (uint256 index = 0; index < routeIdslength; ) {
                                  (bool success, bytes memory result) = addressAt(routeIds[index])
                                      .delegatecall(dataItems[index]);
                                  if (!success) {
                                      assembly {
                                          revert(add(result, 32), mload(result))
                                      }
                                  }
                                  unchecked {
                                      ++index;
                                  }
                              }
                          }
                          /**
                           * @notice execute a controller function identified using the controllerId in the request
                           * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped
                           * @dev ensure the data in request to be built using the function-selector defined as a
                           *         constant in the controller implementation contract
                           * @param socketControllerRequest socketControllerRequest with controllerId to identify the
                           *                                   controllerAddress and byteData constructed using functionSelector
                           *                                   of the function being invoked
                           * @return bytes data received from the call delegated to controller
                           */
                          function executeController(
                              ISocketGateway.SocketControllerRequest calldata socketControllerRequest
                          ) external payable returns (bytes memory) {
                              (bool success, bytes memory result) = controllers[
                                  socketControllerRequest.controllerId
                              ].delegatecall(socketControllerRequest.data);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              return result;
                          }
                          /**
                           * @notice sequentially executes all controller requests
                           * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped
                           * @dev ensure the data in each controller-request to be built using the function-selector defined as a
                           *         constant in the controller implementation contract
                           * @param controllerRequests a list of socketControllerRequest
                           *                              Each controllerRequest contains controllerId to identify the controllerAddress and
                           *                              byteData constructed using functionSelector of the function being invoked
                           */
                          function executeControllers(
                              ISocketGateway.SocketControllerRequest[] calldata controllerRequests
                          ) external payable {
                              for (uint32 index = 0; index < controllerRequests.length; ) {
                                  (bool success, bytes memory result) = controllers[
                                      controllerRequests[index].controllerId
                                  ].delegatecall(controllerRequests[index].data);
                                  if (!success) {
                                      assembly {
                                          revert(add(result, 32), mload(result))
                                      }
                                  }
                                  unchecked {
                                      ++index;
                                  }
                              }
                          }
                          /**************************************
                           *          ADMIN FUNCTIONS           *
                           **************************************/
                          /**
                           * @notice Add route to the socketGateway
                                     This is a restricted function to be called by only socketGatewayOwner
                           * @dev ensure routeAddress is a verified bridge or middleware implementation address
                           * @param routeAddress The address of bridge or middleware implementation contract deployed
                           * @return Id of the route added to the routes-mapping in socketGateway storage
                           */
                          function addRoute(
                              address routeAddress
                          ) external onlyOwner returns (uint32) {
                              uint32 routeId = routesCount;
                              routes[routeId] = routeAddress;
                              routesCount += 1;
                              emit NewRouteAdded(routeId, routeAddress);
                              return routeId;
                          }
                          /**
                           * @notice Give Infinite or 0 approval to bridgeRoute for the tokenAddress
                                     This is a restricted function to be called by only socketGatewayOwner
                           */
                          function setApprovalForRouters(
                              address[] memory routeAddresses,
                              address[] memory tokenAddresses,
                              bool isMax
                          ) external onlyOwner {
                              for (uint32 index = 0; index < routeAddresses.length; ) {
                                  ERC20(tokenAddresses[index]).approve(
                                      routeAddresses[index],
                                      isMax ? type(uint256).max : 0
                                  );
                                  unchecked {
                                      ++index;
                                  }
                              }
                          }
                          /**
                           * @notice Add controller to the socketGateway
                                     This is a restricted function to be called by only socketGatewayOwner
                           * @dev ensure controllerAddress is a verified controller implementation address
                           * @param controllerAddress The address of controller implementation contract deployed
                           * @return Id of the controller added to the controllers-mapping in socketGateway storage
                           */
                          function addController(
                              address controllerAddress
                          ) external onlyOwner returns (uint32) {
                              uint32 controllerId = controllerCount;
                              controllers[controllerId] = controllerAddress;
                              controllerCount += 1;
                              emit ControllerAdded(controllerId, controllerAddress);
                              return controllerId;
                          }
                          /**
                           * @notice disable controller by setting ZeroAddress to the entry in controllers-mapping
                                     identified by controllerId as key.
                                     This is a restricted function to be called by only socketGatewayOwner
                           * @param controllerId The Id of controller-implementation in the controllers mapping
                           */
                          function disableController(uint32 controllerId) public onlyOwner {
                              controllers[controllerId] = disabledRouteAddress;
                              emit ControllerDisabled(controllerId);
                          }
                          /**
                           * @notice disable a route by setting ZeroAddress to the entry in routes-mapping
                                     identified by routeId as key.
                                     This is a restricted function to be called by only socketGatewayOwner
                           * @param routeId The Id of route-implementation in the routes mapping
                           */
                          function disableRoute(uint32 routeId) external onlyOwner {
                              routes[routeId] = disabledRouteAddress;
                              emit RouteDisabled(routeId);
                          }
                          /*******************************************
                           *          RESTRICTED RESCUE FUNCTIONS    *
                           *******************************************/
                          /**
                           * @notice Rescues the ERC20 token to an address
                                     this is a restricted function to be called by only socketGatewayOwner
                           * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address
                           * @param token address of the ERC20 token being rescued
                           * @param userAddress address to which ERC20 is to be rescued
                           * @param amount amount of ERC20 tokens being rescued
                           */
                          function rescueFunds(
                              address token,
                              address userAddress,
                              uint256 amount
                          ) external onlyOwner {
                              ERC20(token).safeTransfer(userAddress, amount);
                          }
                          /**
                           * @notice Rescues the native balance to an address
                                     this is a restricted function to be called by only socketGatewayOwner
                           * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address
                           * @param userAddress address to which native-balance is to be rescued
                           * @param amount amount of native-balance being rescued
                           */
                          function rescueEther(
                              address payable userAddress,
                              uint256 amount
                          ) external onlyOwner {
                              userAddress.transfer(amount);
                          }
                          /*******************************************
                           *          VIEW FUNCTIONS                  *
                           *******************************************/
                          /**
                           * @notice Get routeImplementation address mapped to the routeId
                           * @param routeId routeId is the key in the mapping for routes
                           * @return route-implementation address
                           */
                          function getRoute(uint32 routeId) public view returns (address) {
                              return addressAt(routeId);
                          }
                          /**
                           * @notice Get controllerImplementation address mapped to the controllerId
                           * @param controllerId controllerId is the key in the mapping for controllers
                           * @return controller-implementation address
                           */
                          function getController(uint32 controllerId) public view returns (address) {
                              return controllers[controllerId];
                          }
                          function addressAt(uint32 routeId) public view returns (address) {
                              if (routeId < 385) {
                                  if (routeId < 257) {
                                      if (routeId < 129) {
                                          if (routeId < 65) {
                                              if (routeId < 33) {
                                                  if (routeId < 17) {
                                                      if (routeId < 9) {
                                                          if (routeId < 5) {
                                                              if (routeId < 3) {
                                                                  if (routeId == 1) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 3) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 7) {
                                                                  if (routeId == 5) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 7) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 13) {
                                                              if (routeId < 11) {
                                                                  if (routeId == 9) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 11) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 15) {
                                                                  if (routeId == 13) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 15) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 25) {
                                                          if (routeId < 21) {
                                                              if (routeId < 19) {
                                                                  if (routeId == 17) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 19) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 23) {
                                                                  if (routeId == 21) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 23) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 29) {
                                                              if (routeId < 27) {
                                                                  if (routeId == 25) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 27) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 31) {
                                                                  if (routeId == 29) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 31) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 49) {
                                                      if (routeId < 41) {
                                                          if (routeId < 37) {
                                                              if (routeId < 35) {
                                                                  if (routeId == 33) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 35) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 39) {
                                                                  if (routeId == 37) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 39) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 45) {
                                                              if (routeId < 43) {
                                                                  if (routeId == 41) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 43) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 47) {
                                                                  if (routeId == 45) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 47) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 57) {
                                                          if (routeId < 53) {
                                                              if (routeId < 51) {
                                                                  if (routeId == 49) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 51) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 55) {
                                                                  if (routeId == 53) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 55) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 61) {
                                                              if (routeId < 59) {
                                                                  if (routeId == 57) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 59) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 63) {
                                                                  if (routeId == 61) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 63) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          } else {
                                              if (routeId < 97) {
                                                  if (routeId < 81) {
                                                      if (routeId < 73) {
                                                          if (routeId < 69) {
                                                              if (routeId < 67) {
                                                                  if (routeId == 65) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 67) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 71) {
                                                                  if (routeId == 69) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 71) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 77) {
                                                              if (routeId < 75) {
                                                                  if (routeId == 73) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 75) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 79) {
                                                                  if (routeId == 77) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 79) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 89) {
                                                          if (routeId < 85) {
                                                              if (routeId < 83) {
                                                                  if (routeId == 81) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 83) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 87) {
                                                                  if (routeId == 85) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 87) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 93) {
                                                              if (routeId < 91) {
                                                                  if (routeId == 89) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 91) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 95) {
                                                                  if (routeId == 93) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 95) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 113) {
                                                      if (routeId < 105) {
                                                          if (routeId < 101) {
                                                              if (routeId < 99) {
                                                                  if (routeId == 97) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 99) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 103) {
                                                                  if (routeId == 101) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 103) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 109) {
                                                              if (routeId < 107) {
                                                                  if (routeId == 105) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 107) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 111) {
                                                                  if (routeId == 109) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 111) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 121) {
                                                          if (routeId < 117) {
                                                              if (routeId < 115) {
                                                                  if (routeId == 113) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 115) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 119) {
                                                                  if (routeId == 117) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 119) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 125) {
                                                              if (routeId < 123) {
                                                                  if (routeId == 121) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 123) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 127) {
                                                                  if (routeId == 125) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 127) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          }
                                      } else {
                                          if (routeId < 193) {
                                              if (routeId < 161) {
                                                  if (routeId < 145) {
                                                      if (routeId < 137) {
                                                          if (routeId < 133) {
                                                              if (routeId < 131) {
                                                                  if (routeId == 129) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 131) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 135) {
                                                                  if (routeId == 133) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 135) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 141) {
                                                              if (routeId < 139) {
                                                                  if (routeId == 137) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 139) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 143) {
                                                                  if (routeId == 141) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 143) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 153) {
                                                          if (routeId < 149) {
                                                              if (routeId < 147) {
                                                                  if (routeId == 145) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 147) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 151) {
                                                                  if (routeId == 149) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 151) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 157) {
                                                              if (routeId < 155) {
                                                                  if (routeId == 153) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 155) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 159) {
                                                                  if (routeId == 157) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 159) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 177) {
                                                      if (routeId < 169) {
                                                          if (routeId < 165) {
                                                              if (routeId < 163) {
                                                                  if (routeId == 161) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 163) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 167) {
                                                                  if (routeId == 165) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 167) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 173) {
                                                              if (routeId < 171) {
                                                                  if (routeId == 169) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 171) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 175) {
                                                                  if (routeId == 173) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 175) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 185) {
                                                          if (routeId < 181) {
                                                              if (routeId < 179) {
                                                                  if (routeId == 177) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 179) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 183) {
                                                                  if (routeId == 181) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 183) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 189) {
                                                              if (routeId < 187) {
                                                                  if (routeId == 185) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 187) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 191) {
                                                                  if (routeId == 189) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 191) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          } else {
                                              if (routeId < 225) {
                                                  if (routeId < 209) {
                                                      if (routeId < 201) {
                                                          if (routeId < 197) {
                                                              if (routeId < 195) {
                                                                  if (routeId == 193) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 195) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 199) {
                                                                  if (routeId == 197) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 199) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 205) {
                                                              if (routeId < 203) {
                                                                  if (routeId == 201) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 203) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 207) {
                                                                  if (routeId == 205) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 207) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 217) {
                                                          if (routeId < 213) {
                                                              if (routeId < 211) {
                                                                  if (routeId == 209) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 211) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 215) {
                                                                  if (routeId == 213) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 215) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 221) {
                                                              if (routeId < 219) {
                                                                  if (routeId == 217) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 219) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 223) {
                                                                  if (routeId == 221) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 223) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 241) {
                                                      if (routeId < 233) {
                                                          if (routeId < 229) {
                                                              if (routeId < 227) {
                                                                  if (routeId == 225) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 227) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 231) {
                                                                  if (routeId == 229) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 231) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 237) {
                                                              if (routeId < 235) {
                                                                  if (routeId == 233) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 235) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 239) {
                                                                  if (routeId == 237) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 239) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 249) {
                                                          if (routeId < 245) {
                                                              if (routeId < 243) {
                                                                  if (routeId == 241) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 243) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 247) {
                                                                  if (routeId == 245) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 247) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 253) {
                                                              if (routeId < 251) {
                                                                  if (routeId == 249) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 251) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 255) {
                                                                  if (routeId == 253) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 255) {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  } else {
                                                                      return
                                                                          0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          }
                                      }
                                  } else {
                                      if (routeId < 321) {
                                          if (routeId < 289) {
                                              if (routeId < 273) {
                                                  if (routeId < 265) {
                                                      if (routeId < 261) {
                                                          if (routeId < 259) {
                                                              if (routeId == 257) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 259) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 263) {
                                                              if (routeId == 261) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 263) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 269) {
                                                          if (routeId < 267) {
                                                              if (routeId == 265) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 267) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 271) {
                                                              if (routeId == 269) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 271) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 281) {
                                                      if (routeId < 277) {
                                                          if (routeId < 275) {
                                                              if (routeId == 273) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 275) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 279) {
                                                              if (routeId == 277) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 279) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 285) {
                                                          if (routeId < 283) {
                                                              if (routeId == 281) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 283) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 287) {
                                                              if (routeId == 285) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 287) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          } else {
                                              if (routeId < 305) {
                                                  if (routeId < 297) {
                                                      if (routeId < 293) {
                                                          if (routeId < 291) {
                                                              if (routeId == 289) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 291) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 295) {
                                                              if (routeId == 293) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 295) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 301) {
                                                          if (routeId < 299) {
                                                              if (routeId == 297) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 299) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 303) {
                                                              if (routeId == 301) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 303) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 313) {
                                                      if (routeId < 309) {
                                                          if (routeId < 307) {
                                                              if (routeId == 305) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 307) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 311) {
                                                              if (routeId == 309) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 311) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 317) {
                                                          if (routeId < 315) {
                                                              if (routeId == 313) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 315) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 319) {
                                                              if (routeId == 317) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 319) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          }
                                      } else {
                                          if (routeId < 353) {
                                              if (routeId < 337) {
                                                  if (routeId < 329) {
                                                      if (routeId < 325) {
                                                          if (routeId < 323) {
                                                              if (routeId == 321) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 323) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 327) {
                                                              if (routeId == 325) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 327) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 333) {
                                                          if (routeId < 331) {
                                                              if (routeId == 329) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 331) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 335) {
                                                              if (routeId == 333) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 335) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 345) {
                                                      if (routeId < 341) {
                                                          if (routeId < 339) {
                                                              if (routeId == 337) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 339) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 343) {
                                                              if (routeId == 341) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 343) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 349) {
                                                          if (routeId < 347) {
                                                              if (routeId == 345) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 347) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 351) {
                                                              if (routeId == 349) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 351) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          } else {
                                              if (routeId < 369) {
                                                  if (routeId < 361) {
                                                      if (routeId < 357) {
                                                          if (routeId < 355) {
                                                              if (routeId == 353) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 355) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 359) {
                                                              if (routeId == 357) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 359) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 365) {
                                                          if (routeId < 363) {
                                                              if (routeId == 361) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 363) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 367) {
                                                              if (routeId == 365) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 367) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 377) {
                                                      if (routeId < 373) {
                                                          if (routeId < 371) {
                                                              if (routeId == 369) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 371) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 375) {
                                                              if (routeId == 373) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 375) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 381) {
                                                          if (routeId < 379) {
                                                              if (routeId == 377) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 379) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 383) {
                                                              if (routeId == 381) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          } else {
                                                              if (routeId == 383) {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              } else {
                                                                  return
                                                                      0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          }
                                      }
                                  }
                              }
                              if (routes[routeId] == address(0)) revert ZeroAddressNotAllowed();
                              return routes[routeId];
                          }
                          /// @notice fallback function to handle swap, bridge execution
                          /// @dev ensure routeId is converted to bytes4 and sent as msg.sig in the transaction
                          fallback() external payable {
                              address routeAddress = addressAt(uint32(msg.sig));
                              bytes memory result;
                              assembly {
                                  // copy function selector and any arguments
                                  calldatacopy(0, 4, sub(calldatasize(), 4))
                                  // execute function call using the facet
                                  result := delegatecall(
                                      gas(),
                                      routeAddress,
                                      0,
                                      sub(calldatasize(), 4),
                                      0,
                                      0
                                  )
                                  // get any return value
                                  returndatacopy(0, 0, returndatasize())
                                  // return any return value or error back to the caller
                                  switch result
                                  case 0 {
                                      revert(0, returndatasize())
                                  }
                                  default {
                                      return(0, returndatasize())
                                  }
                              }
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      pragma experimental ABIEncoderV2;
                      import "./utils/Ownable.sol";
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {LibUtil} from "./libraries/LibUtil.sol";
                      import "./libraries/LibBytes.sol";
                      import {ISocketRoute} from "./interfaces/ISocketRoute.sol";
                      import {ISocketRequest} from "./interfaces/ISocketRequest.sol";
                      import {ISocketGateway} from "./interfaces/ISocketGateway.sol";
                      import {IncorrectBridgeRatios, ZeroAddressNotAllowed, ArrayLengthMismatch} from "./errors/SocketErrors.sol";
                      /// @title SocketGatewayContract
                      /// @notice Socketgateway is a contract with entrypoint functions for all interactions with socket liquidity layer
                      /// @author Socket Team
                      contract SocketGateway is Ownable {
                          using LibBytes for bytes;
                          using LibBytes for bytes4;
                          using SafeTransferLib for ERC20;
                          /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation
                          bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =
                              bytes4(keccak256("bridgeAfterSwap(uint256,bytes)"));
                          /// @notice storage variable to keep track of total number of routes registered in socketgateway
                          uint32 public routesCount = 385;
                          /// @notice storage variable to keep track of total number of controllers registered in socketgateway
                          uint32 public controllerCount;
                          address public immutable disabledRouteAddress;
                          uint256 public constant CENT_PERCENT = 100e18;
                          /// @notice storage mapping for route implementation addresses
                          mapping(uint32 => address) public routes;
                          /// storage mapping for controller implemenation addresses
                          mapping(uint32 => address) public controllers;
                          // Events ------------------------------------------------------------------------------------------------------->
                          /// @notice Event emitted when a router is added to socketgateway
                          event NewRouteAdded(uint32 indexed routeId, address indexed route);
                          /// @notice Event emitted when a route is disabled
                          event RouteDisabled(uint32 indexed routeId);
                          /// @notice Event emitted when ownership transfer is requested by socket-gateway-owner
                          event OwnershipTransferRequested(
                              address indexed _from,
                              address indexed _to
                          );
                          /// @notice Event emitted when a controller is added to socketgateway
                          event ControllerAdded(
                              uint32 indexed controllerId,
                              address indexed controllerAddress
                          );
                          /// @notice Event emitted when a controller is disabled
                          event ControllerDisabled(uint32 indexed controllerId);
                          constructor(address _owner, address _disabledRoute) Ownable(_owner) {
                              disabledRouteAddress = _disabledRoute;
                          }
                          // Able to receive ether
                          // solhint-disable-next-line no-empty-blocks
                          receive() external payable {}
                          /*******************************************
                           *          EXTERNAL AND PUBLIC FUNCTIONS  *
                           *******************************************/
                          /**
                           * @notice executes functions in the routes identified using routeId and functionSelectorData
                           * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped
                           * @dev ensure the data in routeData to be built using the function-selector defined as a
                           *         constant in the route implementation contract
                           * @param routeId route identifier
                           * @param routeData functionSelectorData generated using the function-selector defined in the route Implementation
                           */
                          function executeRoute(
                              uint32 routeId,
                              bytes calldata routeData
                          ) external payable returns (bytes memory) {
                              (bool success, bytes memory result) = addressAt(routeId).delegatecall(
                                  routeData
                              );
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              return result;
                          }
                          /**
                           * @notice swaps a token on sourceChain and split it across multiple bridge-recipients
                           * @notice The caller must first approve this contract to spend amount of ERC20-Token being swapped
                           * @dev ensure the swap-data and bridge-data is generated using the function-selector defined as a constant in the implementation address
                           * @param swapMultiBridgeRequest request
                           */
                          function swapAndMultiBridge(
                              ISocketRequest.SwapMultiBridgeRequest calldata swapMultiBridgeRequest
                          ) external payable {
                              uint256 requestLength = swapMultiBridgeRequest.bridgeRouteIds.length;
                              if (
                                  requestLength != swapMultiBridgeRequest.bridgeImplDataItems.length
                              ) {
                                  revert ArrayLengthMismatch();
                              }
                              uint256 ratioAggregate;
                              for (uint256 index = 0; index < requestLength; ) {
                                  ratioAggregate += swapMultiBridgeRequest.bridgeRatios[index];
                              }
                              if (ratioAggregate != CENT_PERCENT) {
                                  revert IncorrectBridgeRatios();
                              }
                              (bool swapSuccess, bytes memory swapResult) = addressAt(
                                  swapMultiBridgeRequest.swapRouteId
                              ).delegatecall(swapMultiBridgeRequest.swapImplData);
                              if (!swapSuccess) {
                                  assembly {
                                      revert(add(swapResult, 32), mload(swapResult))
                                  }
                              }
                              uint256 amountReceivedFromSwap = abi.decode(swapResult, (uint256));
                              uint256 bridgedAmount;
                              for (uint256 index = 0; index < requestLength; ) {
                                  uint256 bridgingAmount;
                                  // if it is the last bridge request, bridge the remaining amount
                                  if (index == requestLength - 1) {
                                      bridgingAmount = amountReceivedFromSwap - bridgedAmount;
                                  } else {
                                      // bridging amount is the multiplication of bridgeRatio and amountReceivedFromSwap
                                      bridgingAmount =
                                          (amountReceivedFromSwap *
                                              swapMultiBridgeRequest.bridgeRatios[index]) /
                                          (CENT_PERCENT);
                                  }
                                  // update the bridged amount, this would be used for computation for last bridgeRequest
                                  bridgedAmount += bridgingAmount;
                                  bytes memory bridgeImpldata = abi.encodeWithSelector(
                                      BRIDGE_AFTER_SWAP_SELECTOR,
                                      bridgingAmount,
                                      swapMultiBridgeRequest.bridgeImplDataItems[index]
                                  );
                                  (bool bridgeSuccess, bytes memory bridgeResult) = addressAt(
                                      swapMultiBridgeRequest.bridgeRouteIds[index]
                                  ).delegatecall(bridgeImpldata);
                                  if (!bridgeSuccess) {
                                      assembly {
                                          revert(add(bridgeResult, 32), mload(bridgeResult))
                                      }
                                  }
                                  unchecked {
                                      ++index;
                                  }
                              }
                          }
                          /**
                           * @notice sequentially executes functions in the routes identified using routeId and functionSelectorData
                           * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped
                           * @dev ensure the data in each dataItem to be built using the function-selector defined as a
                           *         constant in the route implementation contract
                           * @param routeIds a list of route identifiers
                           * @param dataItems a list of functionSelectorData generated using the function-selector defined in the route Implementation
                           */
                          function executeRoutes(
                              uint32[] calldata routeIds,
                              bytes[] calldata dataItems
                          ) external payable {
                              uint256 routeIdslength = routeIds.length;
                              if (routeIdslength != dataItems.length) revert ArrayLengthMismatch();
                              for (uint256 index = 0; index < routeIdslength; ) {
                                  (bool success, bytes memory result) = addressAt(routeIds[index])
                                      .delegatecall(dataItems[index]);
                                  if (!success) {
                                      assembly {
                                          revert(add(result, 32), mload(result))
                                      }
                                  }
                                  unchecked {
                                      ++index;
                                  }
                              }
                          }
                          /**
                           * @notice execute a controller function identified using the controllerId in the request
                           * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped
                           * @dev ensure the data in request to be built using the function-selector defined as a
                           *         constant in the controller implementation contract
                           * @param socketControllerRequest socketControllerRequest with controllerId to identify the
                           *                                   controllerAddress and byteData constructed using functionSelector
                           *                                   of the function being invoked
                           * @return bytes data received from the call delegated to controller
                           */
                          function executeController(
                              ISocketGateway.SocketControllerRequest calldata socketControllerRequest
                          ) external payable returns (bytes memory) {
                              (bool success, bytes memory result) = controllers[
                                  socketControllerRequest.controllerId
                              ].delegatecall(socketControllerRequest.data);
                              if (!success) {
                                  assembly {
                                      revert(add(result, 32), mload(result))
                                  }
                              }
                              return result;
                          }
                          /**
                           * @notice sequentially executes all controller requests
                           * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped
                           * @dev ensure the data in each controller-request to be built using the function-selector defined as a
                           *         constant in the controller implementation contract
                           * @param controllerRequests a list of socketControllerRequest
                           *                              Each controllerRequest contains controllerId to identify the controllerAddress and
                           *                              byteData constructed using functionSelector of the function being invoked
                           */
                          function executeControllers(
                              ISocketGateway.SocketControllerRequest[] calldata controllerRequests
                          ) external payable {
                              for (uint32 index = 0; index < controllerRequests.length; ) {
                                  (bool success, bytes memory result) = controllers[
                                      controllerRequests[index].controllerId
                                  ].delegatecall(controllerRequests[index].data);
                                  if (!success) {
                                      assembly {
                                          revert(add(result, 32), mload(result))
                                      }
                                  }
                                  unchecked {
                                      ++index;
                                  }
                              }
                          }
                          /**************************************
                           *          ADMIN FUNCTIONS           *
                           **************************************/
                          /**
                           * @notice Add route to the socketGateway
                                     This is a restricted function to be called by only socketGatewayOwner
                           * @dev ensure routeAddress is a verified bridge or middleware implementation address
                           * @param routeAddress The address of bridge or middleware implementation contract deployed
                           * @return Id of the route added to the routes-mapping in socketGateway storage
                           */
                          function addRoute(
                              address routeAddress
                          ) external onlyOwner returns (uint32) {
                              uint32 routeId = routesCount;
                              routes[routeId] = routeAddress;
                              routesCount += 1;
                              emit NewRouteAdded(routeId, routeAddress);
                              return routeId;
                          }
                          /**
                           * @notice Give Infinite or 0 approval to bridgeRoute for the tokenAddress
                                     This is a restricted function to be called by only socketGatewayOwner
                           */
                          function setApprovalForRouters(
                              address[] memory routeAddresses,
                              address[] memory tokenAddresses,
                              bool isMax
                          ) external onlyOwner {
                              for (uint32 index = 0; index < routeAddresses.length; ) {
                                  ERC20(tokenAddresses[index]).approve(
                                      routeAddresses[index],
                                      isMax ? type(uint256).max : 0
                                  );
                                  unchecked {
                                      ++index;
                                  }
                              }
                          }
                          /**
                           * @notice Add controller to the socketGateway
                                     This is a restricted function to be called by only socketGatewayOwner
                           * @dev ensure controllerAddress is a verified controller implementation address
                           * @param controllerAddress The address of controller implementation contract deployed
                           * @return Id of the controller added to the controllers-mapping in socketGateway storage
                           */
                          function addController(
                              address controllerAddress
                          ) external onlyOwner returns (uint32) {
                              uint32 controllerId = controllerCount;
                              controllers[controllerId] = controllerAddress;
                              controllerCount += 1;
                              emit ControllerAdded(controllerId, controllerAddress);
                              return controllerId;
                          }
                          /**
                           * @notice disable controller by setting ZeroAddress to the entry in controllers-mapping
                                     identified by controllerId as key.
                                     This is a restricted function to be called by only socketGatewayOwner
                           * @param controllerId The Id of controller-implementation in the controllers mapping
                           */
                          function disableController(uint32 controllerId) public onlyOwner {
                              controllers[controllerId] = disabledRouteAddress;
                              emit ControllerDisabled(controllerId);
                          }
                          /**
                           * @notice disable a route by setting ZeroAddress to the entry in routes-mapping
                                     identified by routeId as key.
                                     This is a restricted function to be called by only socketGatewayOwner
                           * @param routeId The Id of route-implementation in the routes mapping
                           */
                          function disableRoute(uint32 routeId) external onlyOwner {
                              routes[routeId] = disabledRouteAddress;
                              emit RouteDisabled(routeId);
                          }
                          /*******************************************
                           *          RESTRICTED RESCUE FUNCTIONS    *
                           *******************************************/
                          /**
                           * @notice Rescues the ERC20 token to an address
                                     this is a restricted function to be called by only socketGatewayOwner
                           * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address
                           * @param token address of the ERC20 token being rescued
                           * @param userAddress address to which ERC20 is to be rescued
                           * @param amount amount of ERC20 tokens being rescued
                           */
                          function rescueFunds(
                              address token,
                              address userAddress,
                              uint256 amount
                          ) external onlyOwner {
                              ERC20(token).safeTransfer(userAddress, amount);
                          }
                          /**
                           * @notice Rescues the native balance to an address
                                     this is a restricted function to be called by only socketGatewayOwner
                           * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address
                           * @param userAddress address to which native-balance is to be rescued
                           * @param amount amount of native-balance being rescued
                           */
                          function rescueEther(
                              address payable userAddress,
                              uint256 amount
                          ) external onlyOwner {
                              userAddress.transfer(amount);
                          }
                          /*******************************************
                           *          VIEW FUNCTIONS                  *
                           *******************************************/
                          /**
                           * @notice Get routeImplementation address mapped to the routeId
                           * @param routeId routeId is the key in the mapping for routes
                           * @return route-implementation address
                           */
                          function getRoute(uint32 routeId) public view returns (address) {
                              return addressAt(routeId);
                          }
                          /**
                           * @notice Get controllerImplementation address mapped to the controllerId
                           * @param controllerId controllerId is the key in the mapping for controllers
                           * @return controller-implementation address
                           */
                          function getController(uint32 controllerId) public view returns (address) {
                              return controllers[controllerId];
                          }
                          function addressAt(uint32 routeId) public view returns (address) {
                              if (routeId < 385) {
                                  if (routeId < 257) {
                                      if (routeId < 129) {
                                          if (routeId < 65) {
                                              if (routeId < 33) {
                                                  if (routeId < 17) {
                                                      if (routeId < 9) {
                                                          if (routeId < 5) {
                                                              if (routeId < 3) {
                                                                  if (routeId == 1) {
                                                                      return
                                                                          0x8cd6BaCDAe46B449E2e5B34e348A4eD459c84D50;
                                                                  } else {
                                                                      return
                                                                          0x31524750Cd865fF6A3540f232754Fb974c18585C;
                                                                  }
                                                              } else {
                                                                  if (routeId == 3) {
                                                                      return
                                                                          0xEd9b37342BeC8f3a2D7b000732ec87498aA6EC6a;
                                                                  } else {
                                                                      return
                                                                          0xE8704Ef6211F8988Ccbb11badC89841808d66890;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 7) {
                                                                  if (routeId == 5) {
                                                                      return
                                                                          0x9aFF58C460a461578C433e11C4108D1c4cF77761;
                                                                  } else {
                                                                      return
                                                                          0x2D1733886cFd465B0B99F1492F40847495f334C5;
                                                                  }
                                                              } else {
                                                                  if (routeId == 7) {
                                                                      return
                                                                          0x715497Be4D130F04B8442F0A1F7a9312D4e54FC4;
                                                                  } else {
                                                                      return
                                                                          0x90C8a40c38E633B5B0e0d0585b9F7FA05462CaaF;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 13) {
                                                              if (routeId < 11) {
                                                                  if (routeId == 9) {
                                                                      return
                                                                          0xa402b70FCfF3F4a8422B93Ef58E895021eAdE4F6;
                                                                  } else {
                                                                      return
                                                                          0xc1B718522E15CD42C4Ac385a929fc2B51f5B892e;
                                                                  }
                                                              } else {
                                                                  if (routeId == 11) {
                                                                      return
                                                                          0xa97bf2f7c26C43c010c349F52f5eA5dC49B2DD38;
                                                                  } else {
                                                                      return
                                                                          0x969423d71b62C81d2f28d707364c9Dc4a0764c53;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 15) {
                                                                  if (routeId == 13) {
                                                                      return
                                                                          0xF86729934C083fbEc8C796068A1fC60701Ea1207;
                                                                  } else {
                                                                      return
                                                                          0xD7cC2571F5823caCA26A42690D2BE7803DD5393f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 15) {
                                                                      return
                                                                          0x7c8837a279bbbf7d8B93413763176de9F65d5bB9;
                                                                  } else {
                                                                      return
                                                                          0x13b81C27B588C07D04458ed7dDbdbD26D1e39bcc;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 25) {
                                                          if (routeId < 21) {
                                                              if (routeId < 19) {
                                                                  if (routeId == 17) {
                                                                      return
                                                                          0x52560Ac678aFA1345D15474287d16Dc1eA3F78aE;
                                                                  } else {
                                                                      return
                                                                          0x1E31e376551459667cd7643440c1b21CE69065A0;
                                                                  }
                                                              } else {
                                                                  if (routeId == 19) {
                                                                      return
                                                                          0xc57D822CB3288e7b97EF8f8af0EcdcD1B783529B;
                                                                  } else {
                                                                      return
                                                                          0x2197A1D9Af24b4d6a64Bff95B4c29Fcd3Ff28C30;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 23) {
                                                                  if (routeId == 21) {
                                                                      return
                                                                          0xE3700feAa5100041Bf6b7AdBA1f72f647809Fd00;
                                                                  } else {
                                                                      return
                                                                          0xc02E8a0Fdabf0EeFCEA025163d90B5621E2b9948;
                                                                  }
                                                              } else {
                                                                  if (routeId == 23) {
                                                                      return
                                                                          0xF5144235E2926cAb3c69b30113254Fa632f72d62;
                                                                  } else {
                                                                      return
                                                                          0xBa3F92313B00A1f7Bc53b2c24EB195c8b2F57682;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 29) {
                                                              if (routeId < 27) {
                                                                  if (routeId == 25) {
                                                                      return
                                                                          0x77a6856fe1fFA5bEB55A1d2ED86E27C7c482CB76;
                                                                  } else {
                                                                      return
                                                                          0x4826Ff4e01E44b1FCEFBfb38cd96687Eb7786b44;
                                                                  }
                                                              } else {
                                                                  if (routeId == 27) {
                                                                      return
                                                                          0x55FF3f5493cf5e80E76DEA7E327b9Cd8440Af646;
                                                                  } else {
                                                                      return
                                                                          0xF430Db544bE9770503BE4aa51997aA19bBd5BA4f;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 31) {
                                                                  if (routeId == 29) {
                                                                      return
                                                                          0x0f166446ce1484EE3B0663E7E67DF10F5D240115;
                                                                  } else {
                                                                      return
                                                                          0x6365095D92537f242Db5EdFDd572745E72aC33d9;
                                                                  }
                                                              } else {
                                                                  if (routeId == 31) {
                                                                      return
                                                                          0x5c7BC93f06ce3eAe75ADf55E10e23d2c1dE5Bc65;
                                                                  } else {
                                                                      return
                                                                          0xe46383bAD90d7A08197ccF08972e9DCdccCE9BA4;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 49) {
                                                      if (routeId < 41) {
                                                          if (routeId < 37) {
                                                              if (routeId < 35) {
                                                                  if (routeId == 33) {
                                                                      return
                                                                          0xf0f21710c071E3B728bdc4654c3c0b873aAaa308;
                                                                  } else {
                                                                      return
                                                                          0x63Bc9ed3AcAAeB0332531C9fB03b0a2352E9Ff25;
                                                                  }
                                                              } else {
                                                                  if (routeId == 35) {
                                                                      return
                                                                          0xd1CE808625CB4007a1708824AE82CdB0ece57De9;
                                                                  } else {
                                                                      return
                                                                          0x57BbB148112f4ba224841c3FE018884171004661;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 39) {
                                                                  if (routeId == 37) {
                                                                      return
                                                                          0x037f7d6933036F34DFabd40Ff8e4D789069f92e3;
                                                                  } else {
                                                                      return
                                                                          0xeF978c280915CfF3Dca4EDfa8932469e40ADA1e1;
                                                                  }
                                                              } else {
                                                                  if (routeId == 39) {
                                                                      return
                                                                          0x92ee9e071B13f7ecFD62B7DED404A16CBc223CD3;
                                                                  } else {
                                                                      return
                                                                          0x94Ae539c186e41ed762271338Edf140414D1E442;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 45) {
                                                              if (routeId < 43) {
                                                                  if (routeId == 41) {
                                                                      return
                                                                          0x30A64BBe4DdBD43dA2368EFd1eB2d80C10d84DAb;
                                                                  } else {
                                                                      return
                                                                          0x3aEABf81c1Dc4c1b73d5B2a95410f126426FB596;
                                                                  }
                                                              } else {
                                                                  if (routeId == 43) {
                                                                      return
                                                                          0x25b08aB3D0C8ea4cC9d967b79688C6D98f3f563a;
                                                                  } else {
                                                                      return
                                                                          0xea40cB15C9A3BBd27af6474483886F7c0c9AE406;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 47) {
                                                                  if (routeId == 45) {
                                                                      return
                                                                          0x9580113Cc04e5a0a03359686304EF3A80b936Dd3;
                                                                  } else {
                                                                      return
                                                                          0xD211c826d568957F3b66a3F4d9c5f68cCc66E619;
                                                                  }
                                                              } else {
                                                                  if (routeId == 47) {
                                                                      return
                                                                          0xCEE24D0635c4C56315d133b031984d4A6f509476;
                                                                  } else {
                                                                      return
                                                                          0x3922e6B987983229798e7A20095EC372744d4D4c;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 57) {
                                                          if (routeId < 53) {
                                                              if (routeId < 51) {
                                                                  if (routeId == 49) {
                                                                      return
                                                                          0x2d92D03413d296e1F31450479349757187F2a2b7;
                                                                  } else {
                                                                      return
                                                                          0x0fe5308eE90FC78F45c89dB6053eA859097860CA;
                                                                  }
                                                              } else {
                                                                  if (routeId == 51) {
                                                                      return
                                                                          0x08Ba68e067C0505bAF0C1311E0cFB2B1B59b969c;
                                                                  } else {
                                                                      return
                                                                          0x9bee5DdDF75C24897374f92A534B7A6f24e97f4a;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 55) {
                                                                  if (routeId == 53) {
                                                                      return
                                                                          0x1FC5A90B232208704B930c1edf82FFC6ACc02734;
                                                                  } else {
                                                                      return
                                                                          0x5b1B0417cb44c761C2a23ee435d011F0214b3C85;
                                                                  }
                                                              } else {
                                                                  if (routeId == 55) {
                                                                      return
                                                                          0x9d70cDaCA12A738C283020760f449D7816D592ec;
                                                                  } else {
                                                                      return
                                                                          0x95a23b9CB830EcCFDDD5dF56A4ec665e3381Fa12;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 61) {
                                                              if (routeId < 59) {
                                                                  if (routeId == 57) {
                                                                      return
                                                                          0x483a957Cf1251c20e096C35c8399721D1200A3Fc;
                                                                  } else {
                                                                      return
                                                                          0xb4AD39Cb293b0Ec7FEDa743442769A7FF04987CD;
                                                                  }
                                                              } else {
                                                                  if (routeId == 59) {
                                                                      return
                                                                          0x4C543AD78c1590D81BAe09Fc5B6Df4132A2461d0;
                                                                  } else {
                                                                      return
                                                                          0x471d5E5195c563902781734cfe1FF3981F8B6c86;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 63) {
                                                                  if (routeId == 61) {
                                                                      return
                                                                          0x1B12a54B5E606D95B8B8D123c9Cb09221Ee37584;
                                                                  } else {
                                                                      return
                                                                          0xE4127cC550baC433646a7D998775a84daC16c7f3;
                                                                  }
                                                              } else {
                                                                  if (routeId == 63) {
                                                                      return
                                                                          0xecb1b55AB12E7dd788D585c6C5cD61B5F87be836;
                                                                  } else {
                                                                      return
                                                                          0xf91ef487C5A1579f70601b6D347e19756092eEBf;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          } else {
                                              if (routeId < 97) {
                                                  if (routeId < 81) {
                                                      if (routeId < 73) {
                                                          if (routeId < 69) {
                                                              if (routeId < 67) {
                                                                  if (routeId == 65) {
                                                                      return
                                                                          0x34a16a7e9BADEEFD4f056310cbE0b1423Fa1b760;
                                                                  } else {
                                                                      return
                                                                          0x60E10E80c7680f429dBbC232830BEcd3D623c4CF;
                                                                  }
                                                              } else {
                                                                  if (routeId == 67) {
                                                                      return
                                                                          0x66465285B8D65362A1d86CE00fE2bE949Fd6debF;
                                                                  } else {
                                                                      return
                                                                          0x5aB231B7e1A3A74a48f67Ab7bde5Cdd4267022E0;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 71) {
                                                                  if (routeId == 69) {
                                                                      return
                                                                          0x3A1C3633eE79d43366F5c67802a746aFD6b162Ba;
                                                                  } else {
                                                                      return
                                                                          0x0C4BfCbA8dC3C811437521a80E81e41DAF479039;
                                                                  }
                                                              } else {
                                                                  if (routeId == 71) {
                                                                      return
                                                                          0x6caf25d2e139C5431a1FA526EAf8d73ff2e6252C;
                                                                  } else {
                                                                      return
                                                                          0x74ad21e09FDa68638CE14A3009A79B6D16574257;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 77) {
                                                              if (routeId < 75) {
                                                                  if (routeId == 73) {
                                                                      return
                                                                          0xD4923A61008894b99cc1CD3407eF9524f02aA0Ca;
                                                                  } else {
                                                                      return
                                                                          0x6F159b5EB823BD415886b9271aA2A723a00a1987;
                                                                  }
                                                              } else {
                                                                  if (routeId == 75) {
                                                                      return
                                                                          0x742a8aA42E7bfB4554dE30f4Fb07FFb6f2068863;
                                                                  } else {
                                                                      return
                                                                          0x4AE9702d3360400E47B446e76DE063ACAb930101;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 79) {
                                                                  if (routeId == 77) {
                                                                      return
                                                                          0x0E19a0a44ddA7dAD854ec5Cc867d16869c4E80F4;
                                                                  } else {
                                                                      return
                                                                          0xE021A51968f25148F726E326C88d2556c5647557;
                                                                  }
                                                              } else {
                                                                  if (routeId == 79) {
                                                                      return
                                                                          0x64287BDDDaeF4d94E4599a3D882bed29E6Ada4B6;
                                                                  } else {
                                                                      return
                                                                          0xcBB57Fd2e19cc7e9D444d5b4325A2F1047d0C73f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 89) {
                                                          if (routeId < 85) {
                                                              if (routeId < 83) {
                                                                  if (routeId == 81) {
                                                                      return
                                                                          0x373DE80DF7D82cFF6D76F29581b360C56331e957;
                                                                  } else {
                                                                      return
                                                                          0x0466356E131AD61596a51F86BAd1C03A328960D8;
                                                                  }
                                                              } else {
                                                                  if (routeId == 83) {
                                                                      return
                                                                          0x01726B960992f1b74311b248E2a922fC707d43A6;
                                                                  } else {
                                                                      return
                                                                          0x2E21bdf9A4509b89795BCE7E132f248a75814CEc;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 87) {
                                                                  if (routeId == 85) {
                                                                      return
                                                                          0x769512b23aEfF842379091d3B6E4B5456F631D42;
                                                                  } else {
                                                                      return
                                                                          0xe7eD9be946a74Ec19325D39C6EEb57887ccB2B0D;
                                                                  }
                                                              } else {
                                                                  if (routeId == 87) {
                                                                      return
                                                                          0xc4D01Ec357c2b511d10c15e6b6974380F0E62e67;
                                                                  } else {
                                                                      return
                                                                          0x5bC49CC9dD77bECF2fd3A3C55611e84E69AFa3AE;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 93) {
                                                              if (routeId < 91) {
                                                                  if (routeId == 89) {
                                                                      return
                                                                          0x48bcD879954fA14e7DbdAeb56F79C1e9DDcb69ec;
                                                                  } else {
                                                                      return
                                                                          0xE929bDde21b462572FcAA4de6F49B9D3246688D0;
                                                                  }
                                                              } else {
                                                                  if (routeId == 91) {
                                                                      return
                                                                          0x85Aae300438222f0e3A9Bc870267a5633A9438bd;
                                                                  } else {
                                                                      return
                                                                          0x51f72E1096a81C55cd142d66d39B688C657f9Be8;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 95) {
                                                                  if (routeId == 93) {
                                                                      return
                                                                          0x3A8a05BF68ac54B01E6C0f492abF97465F3d15f9;
                                                                  } else {
                                                                      return
                                                                          0x145aA67133F0c2C36b9771e92e0B7655f0D59040;
                                                                  }
                                                              } else {
                                                                  if (routeId == 95) {
                                                                      return
                                                                          0xa030315d7DB11F9892758C9e7092D841e0ADC618;
                                                                  } else {
                                                                      return
                                                                          0xdF1f8d81a3734bdDdEfaC6Ca1596E081e57c3044;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 113) {
                                                      if (routeId < 105) {
                                                          if (routeId < 101) {
                                                              if (routeId < 99) {
                                                                  if (routeId == 97) {
                                                                      return
                                                                          0xFF2833123B58aa05d04D7fb99f5FB768B2b435F8;
                                                                  } else {
                                                                      return
                                                                          0xc8f09c1fD751C570233765f71b0e280d74e6e743;
                                                                  }
                                                              } else {
                                                                  if (routeId == 99) {
                                                                      return
                                                                          0x3026DA6Ceca2E5A57A05153653D9212FFAaA49d8;
                                                                  } else {
                                                                      return
                                                                          0xdE68Ee703dE0D11f67B0cE5891cB4a903de6D160;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 103) {
                                                                  if (routeId == 101) {
                                                                      return
                                                                          0xE23a7730e81FB4E87A6D0bd9f63EE77ac86C3DA4;
                                                                  } else {
                                                                      return
                                                                          0x8b1DBe04aD76a7d8bC079cACd3ED4D99B897F4a0;
                                                                  }
                                                              } else {
                                                                  if (routeId == 103) {
                                                                      return
                                                                          0xBB227240FA459b69C6889B2b8cb1BE76F118061f;
                                                                  } else {
                                                                      return
                                                                          0xC062b9b3f0dB28BB8afAfcD4d075729344114ffe;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 109) {
                                                              if (routeId < 107) {
                                                                  if (routeId == 105) {
                                                                      return
                                                                          0x553188Aa45f5FDB83EC4Ca485982F8fC082480D1;
                                                                  } else {
                                                                      return
                                                                          0x0109d83D746EaCb6d4014953D9E12d6ca85e330b;
                                                                  }
                                                              } else {
                                                                  if (routeId == 107) {
                                                                      return
                                                                          0x45B1bEd29812F5bf6711074ACD180B2aeB783AD9;
                                                                  } else {
                                                                      return
                                                                          0xdA06eC8c19aea31D77F60299678Cba40E743e1aD;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 111) {
                                                                  if (routeId == 109) {
                                                                      return
                                                                          0x3cC5235c97d975a9b4FD4501B3446c981ea3D855;
                                                                  } else {
                                                                      return
                                                                          0xa1827267d6Bd989Ff38580aE3d9deff6Acf19163;
                                                                  }
                                                              } else {
                                                                  if (routeId == 111) {
                                                                      return
                                                                          0x3663CAA0433A3D4171b3581Cf2410702840A735A;
                                                                  } else {
                                                                      return
                                                                          0x7575D0a7614F655BA77C74a72a43bbd4fA6246a3;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 121) {
                                                          if (routeId < 117) {
                                                              if (routeId < 115) {
                                                                  if (routeId == 113) {
                                                                      return
                                                                          0x2516Defc18bc07089c5dAFf5eafD7B0EF64611E2;
                                                                  } else {
                                                                      return
                                                                          0xfec5FF08E20fbc107a97Af2D38BD0025b84ee233;
                                                                  }
                                                              } else {
                                                                  if (routeId == 115) {
                                                                      return
                                                                          0x0FB5763a87242B25243e23D73f55945fE787523A;
                                                                  } else {
                                                                      return
                                                                          0xe4C00db89678dBf8391f430C578Ca857Dd98aDE1;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 119) {
                                                                  if (routeId == 117) {
                                                                      return
                                                                          0x8F2A22061F9F35E64f14523dC1A5f8159e6a21B7;
                                                                  } else {
                                                                      return
                                                                          0x18e4b838ae966917E20E9c9c5Ad359cDD38303bB;
                                                                  }
                                                              } else {
                                                                  if (routeId == 119) {
                                                                      return
                                                                          0x61ACb1d3Dcb3e3429832A164Cc0fC9849fb75A4a;
                                                                  } else {
                                                                      return
                                                                          0x7681e3c8e7A41DCA55C257cc0d1Ae757f5530E65;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 125) {
                                                              if (routeId < 123) {
                                                                  if (routeId == 121) {
                                                                      return
                                                                          0x806a2AB9748C3D1DB976550890E3f528B7E8Faec;
                                                                  } else {
                                                                      return
                                                                          0xBDb8A5DD52C2c239fbC31E9d43B763B0197028FF;
                                                                  }
                                                              } else {
                                                                  if (routeId == 123) {
                                                                      return
                                                                          0x474EC9203706010B9978D6bD0b105D36755e4848;
                                                                  } else {
                                                                      return
                                                                          0x8dfd0D829b303F2239212E591a0F92a32880f36E;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 127) {
                                                                  if (routeId == 125) {
                                                                      return
                                                                          0xad4BcE9745860B1adD6F1Bd34a916f050E4c82C2;
                                                                  } else {
                                                                      return
                                                                          0xBC701115b9fe14bC8CC5934cdC92517173e308C4;
                                                                  }
                                                              } else {
                                                                  if (routeId == 127) {
                                                                      return
                                                                          0x0D1918d786Db8546a11aDeD475C98370E06f255E;
                                                                  } else {
                                                                      return
                                                                          0xee44f57cD6936DB55B99163f3Df367B01EdA785a;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          }
                                      } else {
                                          if (routeId < 193) {
                                              if (routeId < 161) {
                                                  if (routeId < 145) {
                                                      if (routeId < 137) {
                                                          if (routeId < 133) {
                                                              if (routeId < 131) {
                                                                  if (routeId == 129) {
                                                                      return
                                                                          0x63044521fe5a1e488D7eD419cD0e35b7C24F2aa7;
                                                                  } else {
                                                                      return
                                                                          0x410085E73BD85e90d97b84A68C125aDB9F91f85b;
                                                                  }
                                                              } else {
                                                                  if (routeId == 131) {
                                                                      return
                                                                          0x7913fe97E07C7A397Ec274Ab1d4E2622C88EC5D1;
                                                                  } else {
                                                                      return
                                                                          0x977f9fE93c064DCf54157406DaABC3a722e8184C;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 135) {
                                                                  if (routeId == 133) {
                                                                      return
                                                                          0xCD2236468722057cFbbABad2db3DEA9c20d5B01B;
                                                                  } else {
                                                                      return
                                                                          0x17c7287A491cf5Ff81E2678cF2BfAE4333F6108c;
                                                                  }
                                                              } else {
                                                                  if (routeId == 135) {
                                                                      return
                                                                          0x354D9a5Dbf96c71B79a265F03B595C6Fdc04dadd;
                                                                  } else {
                                                                      return
                                                                          0xb4e409EB8e775eeFEb0344f9eee884cc7ed21c69;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 141) {
                                                              if (routeId < 139) {
                                                                  if (routeId == 137) {
                                                                      return
                                                                          0xa1a3c4670Ad69D9be4ab2D39D1231FEC2a63b519;
                                                                  } else {
                                                                      return
                                                                          0x4589A22199870729C1be5CD62EE93BeD858113E6;
                                                                  }
                                                              } else {
                                                                  if (routeId == 139) {
                                                                      return
                                                                          0x8E7b864dB26Bd6C798C38d4Ba36EbA0d6602cF11;
                                                                  } else {
                                                                      return
                                                                          0xA2D17C7260a4CB7b9854e89Fc367E80E87872a2d;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 143) {
                                                                  if (routeId == 141) {
                                                                      return
                                                                          0xC7F0EDf0A1288627b0432304918A75e9084CBD46;
                                                                  } else {
                                                                      return
                                                                          0xE4B4EF1f9A4aBFEdB371fA7a6143993B15d4df25;
                                                                  }
                                                              } else {
                                                                  if (routeId == 143) {
                                                                      return
                                                                          0xfe3D84A2Ef306FEBb5452441C9BDBb6521666F6A;
                                                                  } else {
                                                                      return
                                                                          0x8A12B6C64121920110aE58F7cd67DfEc21c6a4C3;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 153) {
                                                          if (routeId < 149) {
                                                              if (routeId < 147) {
                                                                  if (routeId == 145) {
                                                                      return
                                                                          0x76c4d9aFC4717a2BAac4e5f26CccF02351f7a3DA;
                                                                  } else {
                                                                      return
                                                                          0xd4719BA550E397aeAcca1Ad2201c1ba69024FAAf;
                                                                  }
                                                              } else {
                                                                  if (routeId == 147) {
                                                                      return
                                                                          0x9646126Ce025224d1682C227d915a386efc0A1Fb;
                                                                  } else {
                                                                      return
                                                                          0x4DD8Af2E3F2044842f0247920Bc4BABb636915ea;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 151) {
                                                                  if (routeId == 149) {
                                                                      return
                                                                          0x8e8a327183Af0cf8C2ece9F0ed547C42A160D409;
                                                                  } else {
                                                                      return
                                                                          0x9D49614CaE1C685C71678CA6d8CDF7584bfd0740;
                                                                  }
                                                              } else {
                                                                  if (routeId == 151) {
                                                                      return
                                                                          0x5a00ef257394cbc31828d48655E3d39e9c11c93d;
                                                                  } else {
                                                                      return
                                                                          0xC9a2751b38d3dDD161A41Ca0135C5C6c09EC1d56;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 157) {
                                                              if (routeId < 155) {
                                                                  if (routeId == 153) {
                                                                      return
                                                                          0x7e1c261640a525C94Ca4f8c25b48CF754DD83590;
                                                                  } else {
                                                                      return
                                                                          0x409Fe24ba6F6BD5aF31C1aAf8059b986A3158233;
                                                                  }
                                                              } else {
                                                                  if (routeId == 155) {
                                                                      return
                                                                          0x704Cf5BFDADc0f55fDBb53B6ed8B582E018A72A2;
                                                                  } else {
                                                                      return
                                                                          0x3982bF65d7d6E77E3b6661cd6F6468c247512737;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 159) {
                                                                  if (routeId == 157) {
                                                                      return
                                                                          0x3982b9f26FFD67a13Ee371e2C0a9Da338BA70E7f;
                                                                  } else {
                                                                      return
                                                                          0x6D834AB385900c1f49055D098e90264077FbC4f2;
                                                                  }
                                                              } else {
                                                                  if (routeId == 159) {
                                                                      return
                                                                          0x11FE5F70779A094B7166B391e1Fb73d422eF4e4d;
                                                                  } else {
                                                                      return
                                                                          0xD347e4E47280d21F13B73D89c6d16f867D50DD13;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 177) {
                                                      if (routeId < 169) {
                                                          if (routeId < 165) {
                                                              if (routeId < 163) {
                                                                  if (routeId == 161) {
                                                                      return
                                                                          0xb6035eDD53DDA28d8B69b4ae9836E40C80306CD7;
                                                                  } else {
                                                                      return
                                                                          0x54c884e6f5C7CcfeCA990396c520C858c922b6CA;
                                                                  }
                                                              } else {
                                                                  if (routeId == 163) {
                                                                      return
                                                                          0x5eA93E240b083d686558Ed607BC013d88057cE46;
                                                                  } else {
                                                                      return
                                                                          0x4C7131eE812De685cBe4e2cCb033d46ecD46612E;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 167) {
                                                                  if (routeId == 165) {
                                                                      return
                                                                          0xc1a5Be9F0c33D8483801D702111068669f81fF91;
                                                                  } else {
                                                                      return
                                                                          0x9E5fAb91455Be5E5b2C05967E73F456c8118B1Fc;
                                                                  }
                                                              } else {
                                                                  if (routeId == 167) {
                                                                      return
                                                                          0x3d9A05927223E0DC2F382831770405885e22F0d8;
                                                                  } else {
                                                                      return
                                                                          0x6303A011fB6063f5B1681cb5a9938EA278dc6128;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 173) {
                                                              if (routeId < 171) {
                                                                  if (routeId == 169) {
                                                                      return
                                                                          0xe9c60795c90C66797e4c8E97511eA07CdAda32bE;
                                                                  } else {
                                                                      return
                                                                          0xD56cC98e69A1e13815818b466a8aA6163d84234A;
                                                                  }
                                                              } else {
                                                                  if (routeId == 171) {
                                                                      return
                                                                          0x47EbB9D36a6e40895316cD894E4860D774E2c531;
                                                                  } else {
                                                                      return
                                                                          0xA5EB293629410065d14a7B1663A67829b0618292;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 175) {
                                                                  if (routeId == 173) {
                                                                      return
                                                                          0x1b3B4C8146F939cE00899db8B3ddeF0062b7E023;
                                                                  } else {
                                                                      return
                                                                          0x257Bbc11653625EbfB6A8587eF4f4FBe49828EB3;
                                                                  }
                                                              } else {
                                                                  if (routeId == 175) {
                                                                      return
                                                                          0x44cc979C01b5bB1eAC21301E73C37200dFD06F59;
                                                                  } else {
                                                                      return
                                                                          0x2972fDF43352225D82754C0174Ff853819D1ef2A;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 185) {
                                                          if (routeId < 181) {
                                                              if (routeId < 179) {
                                                                  if (routeId == 177) {
                                                                      return
                                                                          0x3e54144f032648A04D62d79f7B4b93FF3aC2333b;
                                                                  } else {
                                                                      return
                                                                          0x444016102dB8adbE73C3B6703a1ea7F2f75A510D;
                                                                  }
                                                              } else {
                                                                  if (routeId == 179) {
                                                                      return
                                                                          0xac079143f98a6eb744Fde34541ebF243DF5B5dED;
                                                                  } else {
                                                                      return
                                                                          0xAe9010767Fb112d29d35CEdfba2b372Ad7A308d3;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 183) {
                                                                  if (routeId == 181) {
                                                                      return
                                                                          0xfE0BCcF9cCC2265D5fB3450743f17DfE57aE1e56;
                                                                  } else {
                                                                      return
                                                                          0x04ED8C0545716119437a45386B1d691C63234C7D;
                                                                  }
                                                              } else {
                                                                  if (routeId == 183) {
                                                                      return
                                                                          0x636c14013e531A286Bc4C848da34585f0bB73d59;
                                                                  } else {
                                                                      return
                                                                          0x2Fa67fc7ECC5cAA01C653d3BFeA98ecc5db9C42A;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 189) {
                                                              if (routeId < 187) {
                                                                  if (routeId == 185) {
                                                                      return
                                                                          0x23e9a0FC180818aA872D2079a985217017E97bd9;
                                                                  } else {
                                                                      return
                                                                          0x79A95c3Ef81b3ae64ee03A9D5f73e570495F164E;
                                                                  }
                                                              } else {
                                                                  if (routeId == 187) {
                                                                      return
                                                                          0xa7EA0E88F04a84ba0ad1E396cb07Fa3fDAD7dF6D;
                                                                  } else {
                                                                      return
                                                                          0xd23cA1278a2B01a3C0Ca1a00d104b11c1Ebe6f42;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 191) {
                                                                  if (routeId == 189) {
                                                                      return
                                                                          0x707bc4a9FA2E349AED5df4e9f5440C15aA9D14Bd;
                                                                  } else {
                                                                      return
                                                                          0x7E290F2dd539Ac6CE58d8B4C2B944931a1fD3612;
                                                                  }
                                                              } else {
                                                                  if (routeId == 191) {
                                                                      return
                                                                          0x707AA5503088Ce06Ba450B6470A506122eA5c8eF;
                                                                  } else {
                                                                      return
                                                                          0xFbB3f7BF680deeb149f4E7BC30eA3DDfa68F3C3f;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          } else {
                                              if (routeId < 225) {
                                                  if (routeId < 209) {
                                                      if (routeId < 201) {
                                                          if (routeId < 197) {
                                                              if (routeId < 195) {
                                                                  if (routeId == 193) {
                                                                      return
                                                                          0xDE74aD8cCC3dbF14992f49Cf24f36855912f4934;
                                                                  } else {
                                                                      return
                                                                          0x409BA83df7777F070b2B50a10a41DE2468d2a3B3;
                                                                  }
                                                              } else {
                                                                  if (routeId == 195) {
                                                                      return
                                                                          0x5CB7Be90A5DD7CfDa54e87626e254FE8C18255B4;
                                                                  } else {
                                                                      return
                                                                          0x0A684fE12BC64fb72B59d0771a566F49BC090356;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 199) {
                                                                  if (routeId == 197) {
                                                                      return
                                                                          0xDf30048d91F8FA2bCfC54952B92bFA8e161D3360;
                                                                  } else {
                                                                      return
                                                                          0x050825Fff032a547C47061CF0696FDB0f65AEa5D;
                                                                  }
                                                              } else {
                                                                  if (routeId == 199) {
                                                                      return
                                                                          0xd55e671dAC1f03d366d8535073ada5DB2Aab1Ea2;
                                                                  } else {
                                                                      return
                                                                          0x9470C704A9616c8Cd41c595Fcd2181B6fe2183C2;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 205) {
                                                              if (routeId < 203) {
                                                                  if (routeId == 201) {
                                                                      return
                                                                          0x2D9ffD275181F5865d5e11CbB4ced1521C4dF9f1;
                                                                  } else {
                                                                      return
                                                                          0x816d28Dec10ec95DF5334f884dE85cA6215918d8;
                                                                  }
                                                              } else {
                                                                  if (routeId == 203) {
                                                                      return
                                                                          0xd1f87267c4A43835E666dd69Df077e578A3b6299;
                                                                  } else {
                                                                      return
                                                                          0x39E89Bde9DACbe5468C025dE371FbDa12bDeBAB1;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 207) {
                                                                  if (routeId == 205) {
                                                                      return
                                                                          0x7b40A3207956ecad6686E61EfcaC48912FcD0658;
                                                                  } else {
                                                                      return
                                                                          0x090cF10D793B1Efba9c7D76115878814B663859A;
                                                                  }
                                                              } else {
                                                                  if (routeId == 207) {
                                                                      return
                                                                          0x312A59c06E41327878F2063eD0e9c282C1DA3AfC;
                                                                  } else {
                                                                      return
                                                                          0x4F1188f46236DD6B5de11Ebf2a9fF08716E7DeB6;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 217) {
                                                          if (routeId < 213) {
                                                              if (routeId < 211) {
                                                                  if (routeId == 209) {
                                                                      return
                                                                          0x0A6F9a3f4fA49909bBfb4339cbE12B42F53BbBeD;
                                                                  } else {
                                                                      return
                                                                          0x01d13d7aCaCbB955B81935c80ffF31e14BdFa71f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 211) {
                                                                      return
                                                                          0x691a14Fa6C7360422EC56dF5876f84d4eDD7f00A;
                                                                  } else {
                                                                      return
                                                                          0x97Aad18d886d181a9c726B3B6aE15a0A69F5aF73;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 215) {
                                                                  if (routeId == 213) {
                                                                      return
                                                                          0x2917241371D2099049Fa29432DC46735baEC33b4;
                                                                  } else {
                                                                      return
                                                                          0x5F20F20F7890c2e383E29D4147C9695A371165f5;
                                                                  }
                                                              } else {
                                                                  if (routeId == 215) {
                                                                      return
                                                                          0xeC0a60e639958335662C5219A320cCEbb56C6077;
                                                                  } else {
                                                                      return
                                                                          0x96d63CF5062975C09845d17ec672E10255866053;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 221) {
                                                              if (routeId < 219) {
                                                                  if (routeId == 217) {
                                                                      return
                                                                          0xFF57429e57D383939CAB50f09ABBfB63C0e6c9AD;
                                                                  } else {
                                                                      return
                                                                          0x18E393A7c8578fb1e235C242076E50013cDdD0d7;
                                                                  }
                                                              } else {
                                                                  if (routeId == 219) {
                                                                      return
                                                                          0xE7E5238AF5d61f52E9B4ACC025F713d1C0216507;
                                                                  } else {
                                                                      return
                                                                          0x428401D4d0F25A2EE1DA4d5366cB96Ded425D9bD;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 223) {
                                                                  if (routeId == 221) {
                                                                      return
                                                                          0x42E5733551ff1Ee5B48Aa9fc2B61Af9b58C812E6;
                                                                  } else {
                                                                      return
                                                                          0x64Df9c7A0551B056d860Bc2419Ca4c1EF75320bE;
                                                                  }
                                                              } else {
                                                                  if (routeId == 223) {
                                                                      return
                                                                          0x46006925506145611bBf0263243D8627dAf26B0F;
                                                                  } else {
                                                                      return
                                                                          0x8D64BE884314662804eAaB884531f5C50F4d500c;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 241) {
                                                      if (routeId < 233) {
                                                          if (routeId < 229) {
                                                              if (routeId < 227) {
                                                                  if (routeId == 225) {
                                                                      return
                                                                          0x157a62D92D07B5ce221A5429645a03bBaCE85373;
                                                                  } else {
                                                                      return
                                                                          0xaF037D33e1F1F2F87309B425fe8a9d895Ef3722B;
                                                                  }
                                                              } else {
                                                                  if (routeId == 227) {
                                                                      return
                                                                          0x921D1154E494A2f7218a37ad7B17701f94b4B40e;
                                                                  } else {
                                                                      return
                                                                          0xF282b4555186d8Dea51B8b3F947E1E0568d09bc4;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 231) {
                                                                  if (routeId == 229) {
                                                                      return
                                                                          0xa794E2E1869765a4600b3DFd8a4ebcF16350f6B6;
                                                                  } else {
                                                                      return
                                                                          0xFEFb048e20c5652F7940A49B1980E0125Ec4D358;
                                                                  }
                                                              } else {
                                                                  if (routeId == 231) {
                                                                      return
                                                                          0x220104b641971e9b25612a8F001bf48AbB23f1cF;
                                                                  } else {
                                                                      return
                                                                          0xcB9D373Bb54A501B35dd3be5bF4Ba43cA31F7035;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 237) {
                                                              if (routeId < 235) {
                                                                  if (routeId == 233) {
                                                                      return
                                                                          0x37D627F56e3FF36aC316372109ea82E03ac97DAc;
                                                                  } else {
                                                                      return
                                                                          0x4E81355FfB4A271B4EA59ff78da2b61c7833161f;
                                                                  }
                                                              } else {
                                                                  if (routeId == 235) {
                                                                      return
                                                                          0xADd8D65cAF6Cc9ad73127B49E16eA7ac29d91e87;
                                                                  } else {
                                                                      return
                                                                          0x630F9b95626487dfEAe3C97A44DB6C59cF35d996;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 239) {
                                                                  if (routeId == 237) {
                                                                      return
                                                                          0x78CE2BC8238B679680A67FCB98C5A60E4ec17b2D;
                                                                  } else {
                                                                      return
                                                                          0xA38D776028eD1310b9A6b086f67F788201762E21;
                                                                  }
                                                              } else {
                                                                  if (routeId == 239) {
                                                                      return
                                                                          0x7Bb5178827B76B86753Ed62a0d662c72cEcb1bD3;
                                                                  } else {
                                                                      return
                                                                          0x4faC26f61C76eC5c3D43b43eDfAFF0736Ae0e3da;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 249) {
                                                          if (routeId < 245) {
                                                              if (routeId < 243) {
                                                                  if (routeId == 241) {
                                                                      return
                                                                          0x791Bb49bfFA7129D6889FDB27744422Ac4571A85;
                                                                  } else {
                                                                      return
                                                                          0x26766fFEbb5fa564777913A6f101dF019AB32afa;
                                                                  }
                                                              } else {
                                                                  if (routeId == 243) {
                                                                      return
                                                                          0x05e98E5e95b4ECBbbAf3258c3999Cc81ed8048Be;
                                                                  } else {
                                                                      return
                                                                          0xC5c4621e52f1D6A1825A5ed4F95855401a3D9C6b;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 247) {
                                                                  if (routeId == 245) {
                                                                      return
                                                                          0xfcb15f909BA7FC7Ea083503Fb4c1020203c107EB;
                                                                  } else {
                                                                      return
                                                                          0xbD27603279d969c74f2486ad14E71080829DFd38;
                                                                  }
                                                              } else {
                                                                  if (routeId == 247) {
                                                                      return
                                                                          0xff2f756BcEcC1A55BFc09a30cc5F64720458cFCB;
                                                                  } else {
                                                                      return
                                                                          0x3bfB968FEbC12F4e8420B2d016EfcE1E615f7246;
                                                                  }
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 253) {
                                                              if (routeId < 251) {
                                                                  if (routeId == 249) {
                                                                      return
                                                                          0x982EE9Ffe23051A2ec945ed676D864fa8345222b;
                                                                  } else {
                                                                      return
                                                                          0xe101899100785E74767d454FFF0131277BaD48d9;
                                                                  }
                                                              } else {
                                                                  if (routeId == 251) {
                                                                      return
                                                                          0x4F730C0c6b3B5B7d06ca511379f4Aa5BfB2E9525;
                                                                  } else {
                                                                      return
                                                                          0x5499c36b365795e4e0Ef671aF6C2ce26D7c78265;
                                                                  }
                                                              }
                                                          } else {
                                                              if (routeId < 255) {
                                                                  if (routeId == 253) {
                                                                      return
                                                                          0x8AF51F7237Fc8fB2fc3E700488a94a0aC6Ad8b5a;
                                                                  } else {
                                                                      return
                                                                          0xda8716df61213c0b143F2849785FB85928084857;
                                                                  }
                                                              } else {
                                                                  if (routeId == 255) {
                                                                      return
                                                                          0xF040Cf9b1ebD11Bf28e04e80740DF3DDe717e4f5;
                                                                  } else {
                                                                      return
                                                                          0xB87ba32f759D14023C7520366B844dF7f0F036C2;
                                                                  }
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          }
                                      }
                                  } else {
                                      if (routeId < 321) {
                                          if (routeId < 289) {
                                              if (routeId < 273) {
                                                  if (routeId < 265) {
                                                      if (routeId < 261) {
                                                          if (routeId < 259) {
                                                              if (routeId == 257) {
                                                                  return
                                                                      0x0Edde681b8478F0c3194f468EdD2dB5e75c65CDD;
                                                              } else {
                                                                  return
                                                                      0x59C70900Fca06eE2aCE1BDd5A8D0Af0cc3BBA720;
                                                              }
                                                          } else {
                                                              if (routeId == 259) {
                                                                  return
                                                                      0x8041F0f180D17dD07087199632c45E17AeB0BAd5;
                                                              } else {
                                                                  return
                                                                      0x4fB4727064BA595995DD516b63b5921Df9B93aC6;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 263) {
                                                              if (routeId == 261) {
                                                                  return
                                                                      0x86e98b594565857eD098864F560915C0dAfd6Ea1;
                                                              } else {
                                                                  return
                                                                      0x70f8818E8B698EFfeCd86A513a4c87c0c380Bef6;
                                                              }
                                                          } else {
                                                              if (routeId == 263) {
                                                                  return
                                                                      0x78Ed227c8A897A21Da2875a752142dd80d865158;
                                                              } else {
                                                                  return
                                                                      0xd02A30BB5C3a8C51d2751A029a6fcfDE2Af9fbc6;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 269) {
                                                          if (routeId < 267) {
                                                              if (routeId == 265) {
                                                                  return
                                                                      0x0F00d5c5acb24e975e2a56730609f7F40aa763b8;
                                                              } else {
                                                                  return
                                                                      0xC3e2091edc2D3D9D98ba09269138b617B536834A;
                                                              }
                                                          } else {
                                                              if (routeId == 267) {
                                                                  return
                                                                      0xa6FbaF7F30867C9633908998ea8C3da28920E75C;
                                                              } else {
                                                                  return
                                                                      0xE6dDdcD41E2bBe8122AE32Ac29B8fbAB79CD21d9;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 271) {
                                                              if (routeId == 269) {
                                                                  return
                                                                      0x537aa8c1Ef6a8Eaf039dd6e1Eb67694a48195cE4;
                                                              } else {
                                                                  return
                                                                      0x96ABAC485fd2D0B03CF4a10df8BD58b8dED28300;
                                                              }
                                                          } else {
                                                              if (routeId == 271) {
                                                                  return
                                                                      0xda8e7D46d04Bd4F62705Cd80355BDB6d441DafFD;
                                                              } else {
                                                                  return
                                                                      0xbE50018E7a5c67E2e5f5414393e971CC96F293f2;
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 281) {
                                                      if (routeId < 277) {
                                                          if (routeId < 275) {
                                                              if (routeId == 273) {
                                                                  return
                                                                      0xa1b3907D6CB542a4cbe2eE441EfFAA909FAb62C3;
                                                              } else {
                                                                  return
                                                                      0x6d08ee8511C0237a515013aC389e7B3968Cb1753;
                                                              }
                                                          } else {
                                                              if (routeId == 275) {
                                                                  return
                                                                      0x22faa5B5Fe43eAdbB52745e35a5cdA8bD5F96bbA;
                                                              } else {
                                                                  return
                                                                      0x7a673eB74D79e4868D689E7852abB5f93Ec2fD4b;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 279) {
                                                              if (routeId == 277) {
                                                                  return
                                                                      0x0b8531F8AFD4190b76F3e10deCaDb84c98b4d419;
                                                              } else {
                                                                  return
                                                                      0x78eABC743A93583DeE403D6b84795490e652216B;
                                                              }
                                                          } else {
                                                              if (routeId == 279) {
                                                                  return
                                                                      0x3A95D907b2a7a8604B59BccA08585F58Afe0Aa64;
                                                              } else {
                                                                  return
                                                                      0xf4271f0C8c9Af0F06A80b8832fa820ccE64FAda8;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 285) {
                                                          if (routeId < 283) {
                                                              if (routeId == 281) {
                                                                  return
                                                                      0x74b2DF841245C3748c0d31542e1335659a25C33b;
                                                              } else {
                                                                  return
                                                                      0xdFC99Fd0Ad7D16f30f295a5EEFcE029E04d0fa65;
                                                              }
                                                          } else {
                                                              if (routeId == 283) {
                                                                  return
                                                                      0xE992416b6aC1144eD8148a9632973257839027F6;
                                                              } else {
                                                                  return
                                                                      0x54ce55ba954E981BB1fd9399054B35Ce1f2C0816;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 287) {
                                                              if (routeId == 285) {
                                                                  return
                                                                      0xD4AB52f9e7E5B315Bd7471920baD04F405Ab1c38;
                                                              } else {
                                                                  return
                                                                      0x3670C990994d12837e95eE127fE2f06FD3E2104B;
                                                              }
                                                          } else {
                                                              if (routeId == 287) {
                                                                  return
                                                                      0xDcf190B09C47E4f551E30BBb79969c3FdEA1e992;
                                                              } else {
                                                                  return
                                                                      0xa65057B967B59677237e57Ab815B209744b9bc40;
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          } else {
                                              if (routeId < 305) {
                                                  if (routeId < 297) {
                                                      if (routeId < 293) {
                                                          if (routeId < 291) {
                                                              if (routeId == 289) {
                                                                  return
                                                                      0x6Efc86B40573e4C7F28659B13327D55ae955C483;
                                                              } else {
                                                                  return
                                                                      0x06BcC25CF8e0E72316F53631b3aA7134E9f73Ae0;
                                                              }
                                                          } else {
                                                              if (routeId == 291) {
                                                                  return
                                                                      0x710b6414E1D53882b1FCD3A168aD5Ccd435fc6D0;
                                                              } else {
                                                                  return
                                                                      0x5Ebb2C3d78c4e9818074559e7BaE7FCc99781DC1;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 295) {
                                                              if (routeId == 293) {
                                                                  return
                                                                      0xAf0a409c3AEe0bD08015cfb29D89E90b6e89A88F;
                                                              } else {
                                                                  return
                                                                      0x522559d8b99773C693B80cE06DF559036295Ce44;
                                                              }
                                                          } else {
                                                              if (routeId == 295) {
                                                                  return
                                                                      0xB65290A5Bae838aaa7825c9ECEC68041841a1B64;
                                                              } else {
                                                                  return
                                                                      0x801b8F2068edd5Bcb659E6BDa0c425909043C420;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 301) {
                                                          if (routeId < 299) {
                                                              if (routeId == 297) {
                                                                  return
                                                                      0x29b5F00515d093627E0B7bd0b5c8E84F6b4cDb87;
                                                              } else {
                                                                  return
                                                                      0x652839Ae74683cbF9f1293F1019D938F87464D3E;
                                                              }
                                                          } else {
                                                              if (routeId == 299) {
                                                                  return
                                                                      0x5Bc95dCebDDE9B79F2b6DC76121BC7936eF8D666;
                                                              } else {
                                                                  return
                                                                      0x90db359CEA62E53051158Ab5F99811C0a07Fe686;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 303) {
                                                              if (routeId == 301) {
                                                                  return
                                                                      0x2c3625EedadbDcDbB5330eb0d17b3C39ff269807;
                                                              } else {
                                                                  return
                                                                      0xC3f0324471b5c9d415acD625b8d8694a4e48e001;
                                                              }
                                                          } else {
                                                              if (routeId == 303) {
                                                                  return
                                                                      0x8C60e7E05fa0FfB6F720233736f245134685799d;
                                                              } else {
                                                                  return
                                                                      0x98fAF2c09aa4EBb995ad0B56152993E7291a500e;
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 313) {
                                                      if (routeId < 309) {
                                                          if (routeId < 307) {
                                                              if (routeId == 305) {
                                                                  return
                                                                      0x802c1063a861414dFAEc16bacb81429FC0d40D6e;
                                                              } else {
                                                                  return
                                                                      0x11C4AeFCC0dC156f64195f6513CB1Fb3Be0Ae056;
                                                              }
                                                          } else {
                                                              if (routeId == 307) {
                                                                  return
                                                                      0xEff1F3258214E31B6B4F640b4389d55715C3Be2B;
                                                              } else {
                                                                  return
                                                                      0x47e379Abe8DDFEA4289aBa01235EFF7E93758fd7;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 311) {
                                                              if (routeId == 309) {
                                                                  return
                                                                      0x3CC26384c3eA31dDc8D9789e8872CeA6F20cD3ff;
                                                              } else {
                                                                  return
                                                                      0xEdd9EFa6c69108FAA4611097d643E20Ba0Ed1634;
                                                              }
                                                          } else {
                                                              if (routeId == 311) {
                                                                  return
                                                                      0xCb93525CA5f3D371F74F3D112bC19526740717B8;
                                                              } else {
                                                                  return
                                                                      0x7071E0124EB4438137e60dF1b8DD8Af1BfB362cF;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 317) {
                                                          if (routeId < 315) {
                                                              if (routeId == 313) {
                                                                  return
                                                                      0x4691096EB0b78C8F4b4A8091E5B66b18e1835c10;
                                                              } else {
                                                                  return
                                                                      0x8d953c9b2d1C2137CF95992079f3A77fCd793272;
                                                              }
                                                          } else {
                                                              if (routeId == 315) {
                                                                  return
                                                                      0xbdCc2A3Bf6e3Ba49ff86595e6b2b8D70d8368c92;
                                                              } else {
                                                                  return
                                                                      0x95E6948aB38c61b2D294E8Bd896BCc4cCC0713cf;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 319) {
                                                              if (routeId == 317) {
                                                                  return
                                                                      0x607b27C881fFEE4Cb95B1c5862FaE7224ccd0b4A;
                                                              } else {
                                                                  return
                                                                      0x09D28aFA166e566A2Ee1cB834ea8e78C7E627eD2;
                                                              }
                                                          } else {
                                                              if (routeId == 319) {
                                                                  return
                                                                      0x9c01449b38bDF0B263818401044Fb1401B29fDfA;
                                                              } else {
                                                                  return
                                                                      0x1F7723599bbB658c051F8A39bE2688388d22ceD6;
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          }
                                      } else {
                                          if (routeId < 353) {
                                              if (routeId < 337) {
                                                  if (routeId < 329) {
                                                      if (routeId < 325) {
                                                          if (routeId < 323) {
                                                              if (routeId == 321) {
                                                                  return
                                                                      0x52B71603f7b8A5d15B4482e965a0619aa3210194;
                                                              } else {
                                                                  return
                                                                      0x01c0f072CB210406653752FecFA70B42dA9173a2;
                                                              }
                                                          } else {
                                                              if (routeId == 323) {
                                                                  return
                                                                      0x3021142f021E943e57fc1886cAF58D06147D09A6;
                                                              } else {
                                                                  return
                                                                      0xe6f2AF38e76AB09Db59225d97d3E770942D3D842;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 327) {
                                                              if (routeId == 325) {
                                                                  return
                                                                      0x06a25554e5135F08b9e2eD1DEC1fc3CEd52e0B48;
                                                              } else {
                                                                  return
                                                                      0x71d75e670EE3511C8290C705E0620126B710BF8D;
                                                              }
                                                          } else {
                                                              if (routeId == 327) {
                                                                  return
                                                                      0x8b9cE142b80FeA7c932952EC533694b1DF9B3c54;
                                                              } else {
                                                                  return
                                                                      0xd7Be24f32f39231116B3fDc483C2A12E1521f73B;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 333) {
                                                          if (routeId < 331) {
                                                              if (routeId == 329) {
                                                                  return
                                                                      0xb40cafBC4797d4Ff64087E087F6D2e661f954CbE;
                                                              } else {
                                                                  return
                                                                      0xBdDCe7771EfEe81893e838f62204A4c76D72757e;
                                                              }
                                                          } else {
                                                              if (routeId == 331) {
                                                                  return
                                                                      0x5d3D299EA7Fd4F39AcDb336E26631Dfee41F9287;
                                                              } else {
                                                                  return
                                                                      0x6BfEE09E1Fc0684e0826A9A0dC1352a14B136FAC;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 335) {
                                                              if (routeId == 333) {
                                                                  return
                                                                      0xd0001bB8E2Cb661436093f96458a4358B5156E3c;
                                                              } else {
                                                                  return
                                                                      0x1867c6485CfD1eD448988368A22bfB17a7747293;
                                                              }
                                                          } else {
                                                              if (routeId == 335) {
                                                                  return
                                                                      0x8997EF9F95dF24aB67703AB6C262aABfeEBE33bD;
                                                              } else {
                                                                  return
                                                                      0x1e39E9E601922deD91BCFc8F78836302133465e2;
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 345) {
                                                      if (routeId < 341) {
                                                          if (routeId < 339) {
                                                              if (routeId == 337) {
                                                                  return
                                                                      0x8A8ec6CeacFf502a782216774E5AF3421562C6ff;
                                                              } else {
                                                                  return
                                                                      0x3B8FC561df5415c8DC01e97Ee6E38435A8F9C40A;
                                                              }
                                                          } else {
                                                              if (routeId == 339) {
                                                                  return
                                                                      0xD5d5f5B37E67c43ceA663aEDADFFc3a93a2065B0;
                                                              } else {
                                                                  return
                                                                      0xCC8F55EC43B4f25013CE1946FBB740c43Be5B96D;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 343) {
                                                              if (routeId == 341) {
                                                                  return
                                                                      0x18f586E816eEeDbb57B8011239150367561B58Fb;
                                                              } else {
                                                                  return
                                                                      0xd0CD802B19c1a52501cb2f07d656e3Cd7B0Ce124;
                                                              }
                                                          } else {
                                                              if (routeId == 343) {
                                                                  return
                                                                      0xe0AeD899b39C6e4f2d83e4913a1e9e0cf6368abE;
                                                              } else {
                                                                  return
                                                                      0x0606e1b6c0f1A398C38825DCcc4678a7Cbc2737c;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 349) {
                                                          if (routeId < 347) {
                                                              if (routeId == 345) {
                                                                  return
                                                                      0x2d188e85b27d18EF80f16686EA1593ABF7Ed2A63;
                                                              } else {
                                                                  return
                                                                      0x64412292fA4A135a3300E24366E99ff59Db2eAc1;
                                                              }
                                                          } else {
                                                              if (routeId == 347) {
                                                                  return
                                                                      0x38b74c173f3733E8b90aAEf0e98B89791266149F;
                                                              } else {
                                                                  return
                                                                      0x36DAA49A79aaEF4E7a217A11530D3cCD84414124;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 351) {
                                                              if (routeId == 349) {
                                                                  return
                                                                      0x10f088FE2C88F90270E4449c46c8B1b232511d58;
                                                              } else {
                                                                  return
                                                                      0x4FeDbd25B58586838ABD17D10272697dF1dC3087;
                                                              }
                                                          } else {
                                                              if (routeId == 351) {
                                                                  return
                                                                      0x685278209248CB058E5cEe93e37f274A80Faf6eb;
                                                              } else {
                                                                  return
                                                                      0xDd9F8F1eeC3955f78168e2Fb2d1e808fa8A8f15b;
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          } else {
                                              if (routeId < 369) {
                                                  if (routeId < 361) {
                                                      if (routeId < 357) {
                                                          if (routeId < 355) {
                                                              if (routeId == 353) {
                                                                  return
                                                                      0x7392aEeFD5825aaC28817031dEEBbFaAA20983D9;
                                                              } else {
                                                                  return
                                                                      0x0Cc182555E00767D6FB8AD161A10d0C04C476d91;
                                                              }
                                                          } else {
                                                              if (routeId == 355) {
                                                                  return
                                                                      0x90E52837d56715c79FD592E8D58bFD20365798b2;
                                                              } else {
                                                                  return
                                                                      0x6F4451DE14049B6770ad5BF4013118529e68A40C;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 359) {
                                                              if (routeId == 357) {
                                                                  return
                                                                      0x89B97ef2aFAb9ed9c7f0FDb095d02E6840b52d9c;
                                                              } else {
                                                                  return
                                                                      0x92A5cC5C42d94d3e23aeB1214fFf43Db2B97759E;
                                                              }
                                                          } else {
                                                              if (routeId == 359) {
                                                                  return
                                                                      0x63ddc52F135A1dcBA831EAaC11C63849F018b739;
                                                              } else {
                                                                  return
                                                                      0x692A691533B571C2c54C1D7F8043A204b3d8120E;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 365) {
                                                          if (routeId < 363) {
                                                              if (routeId == 361) {
                                                                  return
                                                                      0x97c7492CF083969F61C6f302d45c8270391b921c;
                                                              } else {
                                                                  return
                                                                      0xDeFD2B8643553dAd19548eB14fd94A57F4B9e543;
                                                              }
                                                          } else {
                                                              if (routeId == 363) {
                                                                  return
                                                                      0x30645C04205cA3f670B67b02F971B088930ACB8C;
                                                              } else {
                                                                  return
                                                                      0xA6f80ed2d607Cd67aEB4109B64A0BEcc4D7d03CF;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 367) {
                                                              if (routeId == 365) {
                                                                  return
                                                                      0xBbbbC6c276eB3F7E674f2D39301509236001c42f;
                                                              } else {
                                                                  return
                                                                      0xC20E77d349FB40CE88eB01824e2873ad9f681f3C;
                                                              }
                                                          } else {
                                                              if (routeId == 367) {
                                                                  return
                                                                      0x5fCfD9a962De19294467C358C1FA55082285960b;
                                                              } else {
                                                                  return
                                                                      0x4D87BD6a0E4E5cc6332923cb3E85fC71b287F58A;
                                                              }
                                                          }
                                                      }
                                                  }
                                              } else {
                                                  if (routeId < 377) {
                                                      if (routeId < 373) {
                                                          if (routeId < 371) {
                                                              if (routeId == 369) {
                                                                  return
                                                                      0x3AA5B757cd6Dde98214E56D57Dde7fcF0F7aB04E;
                                                              } else {
                                                                  return
                                                                      0xe28eFCE7192e11a2297f44059113C1fD6967b2d4;
                                                              }
                                                          } else {
                                                              if (routeId == 371) {
                                                                  return
                                                                      0x3251cAE10a1Cf246e0808D76ACC26F7B5edA0eE5;
                                                              } else {
                                                                  return
                                                                      0xbA2091cc9357Cf4c4F25D64F30d1b4Ba3A5a174B;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 375) {
                                                              if (routeId == 373) {
                                                                  return
                                                                      0x49c8e1Da9693692096F63C82D11b52d738566d55;
                                                              } else {
                                                                  return
                                                                      0xA0731615aB5FFF451031E9551367A4F7dB27b39c;
                                                              }
                                                          } else {
                                                              if (routeId == 375) {
                                                                  return
                                                                      0xFb214541888671AE1403CecC1D59763a12fc1609;
                                                              } else {
                                                                  return
                                                                      0x1D6bCB17642E2336405df73dF22F07688cAec020;
                                                              }
                                                          }
                                                      }
                                                  } else {
                                                      if (routeId < 381) {
                                                          if (routeId < 379) {
                                                              if (routeId == 377) {
                                                                  return
                                                                      0xfC9c0C7bfe187120fF7f4E21446161794A617a9e;
                                                              } else {
                                                                  return
                                                                      0xBa5bF37678EeE2dAB17AEf9D898153258252250E;
                                                              }
                                                          } else {
                                                              if (routeId == 379) {
                                                                  return
                                                                      0x7c55690bd2C9961576A32c02f8EB29ed36415Ec7;
                                                              } else {
                                                                  return
                                                                      0xcA40073E868E8Bc611aEc8Fe741D17E68Fe422f6;
                                                              }
                                                          }
                                                      } else {
                                                          if (routeId < 383) {
                                                              if (routeId == 381) {
                                                                  return
                                                                      0x31641bAFb87E9A58f78835050a7BE56921986339;
                                                              } else {
                                                                  return
                                                                      0xA54766424f6dA74b45EbCc5Bf0Bd1D74D2CCcaAB;
                                                              }
                                                          } else {
                                                              if (routeId == 383) {
                                                                  return
                                                                      0xc7bBa57F8C179EDDBaa62117ddA360e28f3F8252;
                                                              } else {
                                                                  return
                                                                      0x5e663ED97ea77d393B8858C90d0683bF180E0ffd;
                                                              }
                                                          }
                                                      }
                                                  }
                                              }
                                          }
                                      }
                                  }
                              }
                              if (routes[routeId] == address(0)) revert ZeroAddressNotAllowed();
                              return routes[routeId];
                          }
                          /// @notice fallback function to handle swap, bridge execution
                          /// @dev ensure routeId is converted to bytes4 and sent as msg.sig in the transaction
                          fallback() external payable {
                              address routeAddress = addressAt(uint32(msg.sig));
                              bytes memory result;
                              assembly {
                                  // copy function selector and any arguments
                                  calldatacopy(0, 4, sub(calldatasize(), 4))
                                  // execute function call using the facet
                                  result := delegatecall(
                                      gas(),
                                      routeAddress,
                                      0,
                                      sub(calldatasize(), 4),
                                      0,
                                      0
                                  )
                                  // get any return value
                                  returndatacopy(0, 0, returndatasize())
                                  // return any return value or error back to the caller
                                  switch result
                                  case 0 {
                                      revert(0, returndatasize())
                                  }
                                  default {
                                      return(0, returndatasize())
                                  }
                              }
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      bytes32 constant ACROSS = keccak256("Across");
                      bytes32 constant ANYSWAP = keccak256("Anyswap");
                      bytes32 constant CBRIDGE = keccak256("CBridge");
                      bytes32 constant HOP = keccak256("Hop");
                      bytes32 constant HYPHEN = keccak256("Hyphen");
                      bytes32 constant NATIVE_OPTIMISM = keccak256("NativeOptimism");
                      bytes32 constant NATIVE_ARBITRUM = keccak256("NativeArbitrum");
                      bytes32 constant NATIVE_POLYGON = keccak256("NativePolygon");
                      bytes32 constant REFUEL = keccak256("Refuel");
                      bytes32 constant STARGATE = keccak256("Stargate");
                      bytes32 constant ONEINCH = keccak256("OneInch");
                      bytes32 constant ZEROX = keccak256("Zerox");
                      bytes32 constant RAINBOW = keccak256("Rainbow");
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.0;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import "../SwapImplBase.sol";
                      import {SwapFailed} from "../../errors/SocketErrors.sol";
                      import {ONEINCH} from "../../static/RouteIdentifiers.sol";
                      /**
                       * @title OneInch-Swap-Route Implementation
                       * @notice Route implementation with functions to swap tokens via OneInch-Swap
                       * Called via SocketGateway if the routeId in the request maps to the routeId of OneInchImplementation
                       * @author Socket dot tech.
                       */
                      contract OneInchImpl is SwapImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable OneInchIdentifier = ONEINCH;
                          /// @notice address of OneInchAggregator to swap the tokens on Chain
                          address public immutable ONEINCH_AGGREGATOR;
                          /// @notice socketGatewayAddress to be initialised via storage variable SwapImplBase
                          /// @dev ensure _oneinchAggregator are set properly for the chainId in which the contract is being deployed
                          constructor(
                              address _oneinchAggregator,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) SwapImplBase(_socketGateway, _socketDeployFactory) {
                              ONEINCH_AGGREGATOR = _oneinchAggregator;
                          }
                          /**
                           * @notice function to swap tokens on the chain and transfer to receiver address
                           *         via OneInch-Middleware-Aggregator
                           * @param fromToken token to be swapped
                           * @param toToken token to which fromToken has to be swapped
                           * @param amount amount of fromToken being swapped
                           * @param receiverAddress address of toToken recipient
                           * @param swapExtraData encoded value of properties in the swapData Struct
                           * @return swapped amount (in toToken Address)
                           */
                          function performAction(
                              address fromToken,
                              address toToken,
                              uint256 amount,
                              address receiverAddress,
                              bytes calldata swapExtraData
                          ) external payable override returns (uint256) {
                              uint256 returnAmount;
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  ERC20 token = ERC20(fromToken);
                                  token.safeTransferFrom(msg.sender, socketGateway, amount);
                                  token.safeApprove(ONEINCH_AGGREGATOR, amount);
                                  {
                                      // additional data is generated in off-chain using the OneInch API which takes in
                                      // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate
                                      (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call(
                                          swapExtraData
                                      );
                                      token.safeApprove(ONEINCH_AGGREGATOR, 0);
                                      if (!success) {
                                          revert SwapFailed();
                                      }
                                      returnAmount = abi.decode(result, (uint256));
                                  }
                              } else {
                                  // additional data is generated in off-chain using the OneInch API which takes in
                                  // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate
                                  (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call{
                                      value: amount
                                  }(swapExtraData);
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                                  returnAmount = abi.decode(result, (uint256));
                              }
                              emit SocketSwapTokens(
                                  fromToken,
                                  toToken,
                                  returnAmount,
                                  amount,
                                  OneInchIdentifier,
                                  receiverAddress
                              );
                              return returnAmount;
                          }
                          /**
                           * @notice function to swapWithIn SocketGateway - swaps tokens on the chain to socketGateway as recipient
                           *         via OneInch-Middleware-Aggregator
                           * @param fromToken token to be swapped
                           * @param toToken token to which fromToken has to be swapped
                           * @param amount amount of fromToken being swapped
                           * @param swapExtraData encoded value of properties in the swapData Struct
                           * @return swapped amount (in toToken Address)
                           */
                          function performActionWithIn(
                              address fromToken,
                              address toToken,
                              uint256 amount,
                              bytes calldata swapExtraData
                          ) external payable override returns (uint256, address) {
                              uint256 returnAmount;
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  ERC20 token = ERC20(fromToken);
                                  token.safeTransferFrom(msg.sender, socketGateway, amount);
                                  token.safeApprove(ONEINCH_AGGREGATOR, amount);
                                  {
                                      // additional data is generated in off-chain using the OneInch API which takes in
                                      // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate
                                      (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call(
                                          swapExtraData
                                      );
                                      token.safeApprove(ONEINCH_AGGREGATOR, 0);
                                      if (!success) {
                                          revert SwapFailed();
                                      }
                                      returnAmount = abi.decode(result, (uint256));
                                  }
                              } else {
                                  // additional data is generated in off-chain using the OneInch API which takes in
                                  // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate
                                  (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call{
                                      value: amount
                                  }(swapExtraData);
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                                  returnAmount = abi.decode(result, (uint256));
                              }
                              emit SocketSwapTokens(
                                  fromToken,
                                  toToken,
                                  returnAmount,
                                  amount,
                                  OneInchIdentifier,
                                  socketGateway
                              );
                              return (returnAmount, toToken);
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.0;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import "../SwapImplBase.sol";
                      import {Address0Provided, SwapFailed} from "../../errors/SocketErrors.sol";
                      import {RAINBOW} from "../../static/RouteIdentifiers.sol";
                      /**
                       * @title Rainbow-Swap-Route Implementation
                       * @notice Route implementation with functions to swap tokens via Rainbow-Swap
                       * Called via SocketGateway if the routeId in the request maps to the routeId of RainbowImplementation
                       * @author Socket dot tech.
                       */
                      contract RainbowSwapImpl is SwapImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable RainbowIdentifier = RAINBOW;
                          /// @notice unique name to identify the router, used to emit event upon successful bridging
                          bytes32 public immutable NAME = keccak256("Rainbow-Router");
                          /// @notice address of rainbow-swap-aggregator to swap the tokens on Chain
                          address payable public immutable rainbowSwapAggregator;
                          /// @notice socketGatewayAddress to be initialised via storage variable SwapImplBase
                          /// @notice rainbow swap aggregator contract is payable to allow ethereum swaps
                          /// @dev ensure _rainbowSwapAggregator are set properly for the chainId in which the contract is being deployed
                          constructor(
                              address _rainbowSwapAggregator,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) SwapImplBase(_socketGateway, _socketDeployFactory) {
                              rainbowSwapAggregator = payable(_rainbowSwapAggregator);
                          }
                          receive() external payable {}
                          fallback() external payable {}
                          /**
                           * @notice function to swap tokens on the chain and transfer to receiver address
                           * @notice This method is payable because the caller is doing token transfer and swap operation
                           * @param fromToken address of token being Swapped
                           * @param toToken address of token that recipient will receive after swap
                           * @param amount amount of fromToken being swapped
                           * @param receiverAddress recipient-address
                           * @param swapExtraData additional Data to perform Swap via Rainbow-Aggregator
                           * @return swapped amount (in toToken Address)
                           */
                          function performAction(
                              address fromToken,
                              address toToken,
                              uint256 amount,
                              address receiverAddress,
                              bytes calldata swapExtraData
                          ) external payable override returns (uint256) {
                              if (fromToken == address(0)) {
                                  revert Address0Provided();
                              }
                              bytes memory swapCallData = abi.decode(swapExtraData, (bytes));
                              uint256 _initialBalanceTokenOut;
                              uint256 _finalBalanceTokenOut;
                              ERC20 toTokenERC20 = ERC20(toToken);
                              if (toToken != NATIVE_TOKEN_ADDRESS) {
                                  _initialBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);
                              } else {
                                  _initialBalanceTokenOut = address(this).balance;
                              }
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  ERC20 token = ERC20(fromToken);
                                  token.safeTransferFrom(msg.sender, socketGateway, amount);
                                  token.safeApprove(rainbowSwapAggregator, amount);
                                  // solhint-disable-next-line
                                  (bool success, ) = rainbowSwapAggregator.call(swapCallData);
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                                  token.safeApprove(rainbowSwapAggregator, 0);
                              } else {
                                  (bool success, ) = rainbowSwapAggregator.call{value: amount}(
                                      swapCallData
                                  );
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                              }
                              if (toToken != NATIVE_TOKEN_ADDRESS) {
                                  _finalBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);
                              } else {
                                  _finalBalanceTokenOut = address(this).balance;
                              }
                              uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;
                              if (toToken == NATIVE_TOKEN_ADDRESS) {
                                  payable(receiverAddress).transfer(returnAmount);
                              } else {
                                  toTokenERC20.transfer(receiverAddress, returnAmount);
                              }
                              emit SocketSwapTokens(
                                  fromToken,
                                  toToken,
                                  returnAmount,
                                  amount,
                                  RainbowIdentifier,
                                  receiverAddress
                              );
                              return returnAmount;
                          }
                          /**
                           * @notice function to swapWithIn SocketGateway - swaps tokens on the chain to socketGateway as recipient
                           * @param fromToken token to be swapped
                           * @param toToken token to which fromToken has to be swapped
                           * @param amount amount of fromToken being swapped
                           * @param swapExtraData encoded value of properties in the swapData Struct
                           * @return swapped amount (in toToken Address)
                           */
                          function performActionWithIn(
                              address fromToken,
                              address toToken,
                              uint256 amount,
                              bytes calldata swapExtraData
                          ) external payable override returns (uint256, address) {
                              if (fromToken == address(0)) {
                                  revert Address0Provided();
                              }
                              bytes memory swapCallData = abi.decode(swapExtraData, (bytes));
                              uint256 _initialBalanceTokenOut;
                              uint256 _finalBalanceTokenOut;
                              ERC20 toTokenERC20 = ERC20(toToken);
                              if (toToken != NATIVE_TOKEN_ADDRESS) {
                                  _initialBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);
                              } else {
                                  _initialBalanceTokenOut = address(this).balance;
                              }
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  ERC20 token = ERC20(fromToken);
                                  token.safeTransferFrom(msg.sender, socketGateway, amount);
                                  token.safeApprove(rainbowSwapAggregator, amount);
                                  // solhint-disable-next-line
                                  (bool success, ) = rainbowSwapAggregator.call(swapCallData);
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                                  token.safeApprove(rainbowSwapAggregator, 0);
                              } else {
                                  (bool success, ) = rainbowSwapAggregator.call{value: amount}(
                                      swapCallData
                                  );
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                              }
                              if (toToken != NATIVE_TOKEN_ADDRESS) {
                                  _finalBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);
                              } else {
                                  _finalBalanceTokenOut = address(this).balance;
                              }
                              uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;
                              emit SocketSwapTokens(
                                  fromToken,
                                  toToken,
                                  returnAmount,
                                  amount,
                                  RainbowIdentifier,
                                  socketGateway
                              );
                              return (returnAmount, toToken);
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity ^0.8.4;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import {ISocketGateway} from "../interfaces/ISocketGateway.sol";
                      import {OnlySocketGatewayOwner, OnlySocketDeployer} from "../errors/SocketErrors.sol";
                      /**
                       * @title Abstract Implementation Contract.
                       * @notice All Swap Implementation will follow this interface.
                       * @author Socket dot tech.
                       */
                      abstract contract SwapImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          /// @notice Address used to identify if it is a native token transfer or not
                          address public immutable NATIVE_TOKEN_ADDRESS =
                              address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
                          /// @notice immutable variable to store the socketGateway address
                          address public immutable socketGateway;
                          /// @notice immutable variable to store the socketGateway address
                          address public immutable socketDeployFactory;
                          /// @notice FunctionSelector used to delegatecall to the performAction function of swap-router-implementation
                          bytes4 public immutable SWAP_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256("performAction(address,address,uint256,address,bytes)")
                              );
                          /// @notice FunctionSelector used to delegatecall to the performActionWithIn function of swap-router-implementation
                          bytes4 public immutable SWAP_WITHIN_FUNCTION_SELECTOR =
                              bytes4(keccak256("performActionWithIn(address,address,uint256,bytes)"));
                          /****************************************
                           *               EVENTS                 *
                           ****************************************/
                          event SocketSwapTokens(
                              address fromToken,
                              address toToken,
                              uint256 buyAmount,
                              uint256 sellAmount,
                              bytes32 routeName,
                              address receiver
                          );
                          /**
                           * @notice Construct the base for all SwapImplementations.
                           * @param _socketGateway Socketgateway address, an immutable variable to set.
                           */
                          constructor(address _socketGateway, address _socketDeployFactory) {
                              socketGateway = _socketGateway;
                              socketDeployFactory = _socketDeployFactory;
                          }
                          /****************************************
                           *               MODIFIERS              *
                           ****************************************/
                          /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used
                          modifier isSocketGatewayOwner() {
                              if (msg.sender != ISocketGateway(socketGateway).owner()) {
                                  revert OnlySocketGatewayOwner();
                              }
                              _;
                          }
                          /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used
                          modifier isSocketDeployFactory() {
                              if (msg.sender != socketDeployFactory) {
                                  revert OnlySocketDeployer();
                              }
                              _;
                          }
                          /****************************************
                           *    RESTRICTED FUNCTIONS              *
                           ****************************************/
                          /**
                           * @notice function to rescue the ERC20 tokens in the Swap-Implementation contract
                           * @notice this is a function restricted to Owner of SocketGateway only
                           * @param token address of ERC20 token being rescued
                           * @param userAddress receipient address to which ERC20 tokens will be rescued to
                           * @param amount amount of ERC20 tokens being rescued
                           */
                          function rescueFunds(
                              address token,
                              address userAddress,
                              uint256 amount
                          ) external isSocketGatewayOwner {
                              ERC20(token).safeTransfer(userAddress, amount);
                          }
                          /**
                           * @notice function to rescue the native-balance in the  Swap-Implementation contract
                           * @notice this is a function restricted to Owner of SocketGateway only
                           * @param userAddress receipient address to which native-balance will be rescued to
                           * @param amount amount of native balance tokens being rescued
                           */
                          function rescueEther(
                              address payable userAddress,
                              uint256 amount
                          ) external isSocketGatewayOwner {
                              userAddress.transfer(amount);
                          }
                          function killme() external isSocketDeployFactory {
                              selfdestruct(payable(msg.sender));
                          }
                          /******************************
                           *    VIRTUAL FUNCTIONS       *
                           *****************************/
                          /**
                           * @notice function to swap tokens on the chain
                           *         All swap implementation contracts must implement this function
                           * @param fromToken token to be swapped
                           * @param  toToken token to which fromToken has to be swapped
                           * @param amount amount of fromToken being swapped
                           * @param receiverAddress recipient address of toToken
                           * @param data encoded value of properties in the swapData Struct
                           */
                          function performAction(
                              address fromToken,
                              address toToken,
                              uint256 amount,
                              address receiverAddress,
                              bytes memory data
                          ) external payable virtual returns (uint256);
                          /**
                           * @notice function to swapWith - swaps tokens on the chain to socketGateway as recipient
                           *         All swap implementation contracts must implement this function
                           * @param fromToken token to be swapped
                           * @param toToken token to which fromToken has to be swapped
                           * @param amount amount of fromToken being swapped
                           * @param swapExtraData encoded value of properties in the swapData Struct
                           */
                          function performActionWithIn(
                              address fromToken,
                              address toToken,
                              uint256 amount,
                              bytes memory swapExtraData
                          ) external payable virtual returns (uint256, address);
                      }
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.0;
                      import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
                      import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
                      import "../SwapImplBase.sol";
                      import {Address0Provided, SwapFailed} from "../../errors/SocketErrors.sol";
                      import {ZEROX} from "../../static/RouteIdentifiers.sol";
                      /**
                       * @title ZeroX-Swap-Route Implementation
                       * @notice Route implementation with functions to swap tokens via ZeroX-Swap
                       * Called via SocketGateway if the routeId in the request maps to the routeId of ZeroX-Swap-Implementation
                       * @author Socket dot tech.
                       */
                      contract ZeroXSwapImpl is SwapImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                          bytes32 public immutable ZeroXIdentifier = ZEROX;
                          /// @notice unique name to identify the router, used to emit event upon successful bridging
                          bytes32 public immutable NAME = keccak256("Zerox-Router");
                          /// @notice address of ZeroX-Exchange-Proxy to swap the tokens on Chain
                          address payable public immutable zeroXExchangeProxy;
                          /// @notice socketGatewayAddress to be initialised via storage variable SwapImplBase
                          /// @notice ZeroXExchangeProxy contract is payable to allow ethereum swaps
                          /// @dev ensure _zeroXExchangeProxy are set properly for the chainId in which the contract is being deployed
                          constructor(
                              address _zeroXExchangeProxy,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) SwapImplBase(_socketGateway, _socketDeployFactory) {
                              zeroXExchangeProxy = payable(_zeroXExchangeProxy);
                          }
                          receive() external payable {}
                          fallback() external payable {}
                          /**
                           * @notice function to swap tokens on the chain and transfer to receiver address
                           * @dev This is called only when there is a request for a swap.
                           * @param fromToken token to be swapped
                           * @param toToken token to which fromToken is to be swapped
                           * @param amount amount to be swapped
                           * @param receiverAddress address of toToken recipient
                           * @param swapExtraData data required for zeroX Exchange to get the swap done
                           */
                          function performAction(
                              address fromToken,
                              address toToken,
                              uint256 amount,
                              address receiverAddress,
                              bytes calldata swapExtraData
                          ) external payable override returns (uint256) {
                              if (fromToken == address(0)) {
                                  revert Address0Provided();
                              }
                              bytes memory swapCallData = abi.decode(swapExtraData, (bytes));
                              uint256 _initialBalanceTokenOut;
                              uint256 _finalBalanceTokenOut;
                              ERC20 erc20ToToken = ERC20(toToken);
                              if (toToken != NATIVE_TOKEN_ADDRESS) {
                                  _initialBalanceTokenOut = erc20ToToken.balanceOf(address(this));
                              } else {
                                  _initialBalanceTokenOut = address(this).balance;
                              }
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  ERC20 token = ERC20(fromToken);
                                  token.safeTransferFrom(msg.sender, address(this), amount);
                                  token.safeApprove(zeroXExchangeProxy, amount);
                                  // solhint-disable-next-line
                                  (bool success, ) = zeroXExchangeProxy.call(swapCallData);
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                                  token.safeApprove(zeroXExchangeProxy, 0);
                              } else {
                                  (bool success, ) = zeroXExchangeProxy.call{value: amount}(
                                      swapCallData
                                  );
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                              }
                              if (toToken != NATIVE_TOKEN_ADDRESS) {
                                  _finalBalanceTokenOut = erc20ToToken.balanceOf(address(this));
                              } else {
                                  _finalBalanceTokenOut = address(this).balance;
                              }
                              uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;
                              if (toToken == NATIVE_TOKEN_ADDRESS) {
                                  payable(receiverAddress).transfer(returnAmount);
                              } else {
                                  erc20ToToken.transfer(receiverAddress, returnAmount);
                              }
                              emit SocketSwapTokens(
                                  fromToken,
                                  toToken,
                                  returnAmount,
                                  amount,
                                  ZeroXIdentifier,
                                  receiverAddress
                              );
                              return returnAmount;
                          }
                          /**
                           * @notice function to swapWithIn SocketGateway - swaps tokens on the chain to socketGateway as recipient
                           * @param fromToken token to be swapped
                           * @param toToken token to which fromToken has to be swapped
                           * @param amount amount of fromToken being swapped
                           * @param swapExtraData encoded value of properties in the swapData Struct
                           * @return swapped amount (in toToken Address)
                           */
                          function performActionWithIn(
                              address fromToken,
                              address toToken,
                              uint256 amount,
                              bytes calldata swapExtraData
                          ) external payable override returns (uint256, address) {
                              if (fromToken == address(0)) {
                                  revert Address0Provided();
                              }
                              bytes memory swapCallData = abi.decode(swapExtraData, (bytes));
                              uint256 _initialBalanceTokenOut;
                              uint256 _finalBalanceTokenOut;
                              ERC20 erc20ToToken = ERC20(toToken);
                              if (toToken != NATIVE_TOKEN_ADDRESS) {
                                  _initialBalanceTokenOut = erc20ToToken.balanceOf(address(this));
                              } else {
                                  _initialBalanceTokenOut = address(this).balance;
                              }
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  ERC20 token = ERC20(fromToken);
                                  token.safeTransferFrom(msg.sender, address(this), amount);
                                  token.safeApprove(zeroXExchangeProxy, amount);
                                  // solhint-disable-next-line
                                  (bool success, ) = zeroXExchangeProxy.call(swapCallData);
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                                  token.safeApprove(zeroXExchangeProxy, 0);
                              } else {
                                  (bool success, ) = zeroXExchangeProxy.call{value: amount}(
                                      swapCallData
                                  );
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                              }
                              if (toToken != NATIVE_TOKEN_ADDRESS) {
                                  _finalBalanceTokenOut = erc20ToToken.balanceOf(address(this));
                              } else {
                                  _finalBalanceTokenOut = address(this).balance;
                              }
                              uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;
                              emit SocketSwapTokens(
                                  fromToken,
                                  toToken,
                                  returnAmount,
                                  amount,
                                  ZeroXIdentifier,
                                  socketGateway
                              );
                              return (returnAmount, toToken);
                          }
                      }
                      // SPDX-License-Identifier: GPL-3.0-only
                      pragma solidity ^0.8.4;
                      import {OnlyOwner, OnlyNominee} from "../errors/SocketErrors.sol";
                      abstract contract Ownable {
                          address private _owner;
                          address private _nominee;
                          event OwnerNominated(address indexed nominee);
                          event OwnerClaimed(address indexed claimer);
                          constructor(address owner_) {
                              _claimOwner(owner_);
                          }
                          modifier onlyOwner() {
                              if (msg.sender != _owner) {
                                  revert OnlyOwner();
                              }
                              _;
                          }
                          function owner() public view returns (address) {
                              return _owner;
                          }
                          function nominee() public view returns (address) {
                              return _nominee;
                          }
                          function nominateOwner(address nominee_) external {
                              if (msg.sender != _owner) {
                                  revert OnlyOwner();
                              }
                              _nominee = nominee_;
                              emit OwnerNominated(_nominee);
                          }
                          function claimOwner() external {
                              if (msg.sender != _nominee) {
                                  revert OnlyNominee();
                              }
                              _claimOwner(msg.sender);
                          }
                          function _claimOwner(address claimer_) internal {
                              _owner = claimer_;
                              _nominee = address(0);
                              emit OwnerClaimed(claimer_);
                          }
                      }
                      

                      File 8 of 12: VerifyingPaymaster
                      // SPDX-License-Identifier: MIT
                      // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
                      pragma solidity ^0.8.0;
                      import "../utils/Context.sol";
                      /**
                       * @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);
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      // 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);
                      }
                      // SPDX-License-Identifier: MIT
                      // 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);
                      }
                      // SPDX-License-Identifier: MIT
                      // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
                      pragma solidity ^0.8.0;
                      import "../IERC20.sol";
                      import "../extensions/draft-IERC20Permit.sol";
                      import "../../../utils/Address.sol";
                      /**
                       * @title SafeERC20
                       * @dev Wrappers around ERC20 operations that throw on failure (when the token
                       * contract returns false). Tokens that return no value (and instead revert or
                       * throw on failure) are also supported, non-reverting calls are assumed to be
                       * successful.
                       * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                       * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                       */
                      library SafeERC20 {
                          using Address for address;
                          function safeTransfer(
                              IERC20 token,
                              address to,
                              uint256 value
                          ) internal {
                              _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                          }
                          function safeTransferFrom(
                              IERC20 token,
                              address from,
                              address to,
                              uint256 value
                          ) internal {
                              _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                          }
                          /**
                           * @dev Deprecated. This function has issues similar to the ones found in
                           * {IERC20-approve}, and its usage is discouraged.
                           *
                           * Whenever possible, use {safeIncreaseAllowance} and
                           * {safeDecreaseAllowance} instead.
                           */
                          function safeApprove(
                              IERC20 token,
                              address spender,
                              uint256 value
                          ) internal {
                              // safeApprove should only be called when setting an initial allowance,
                              // or when resetting it to zero. To increase and decrease it, use
                              // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                              require(
                                  (value == 0) || (token.allowance(address(this), spender) == 0),
                                  "SafeERC20: approve from non-zero to non-zero allowance"
                              );
                              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                          }
                          function safeIncreaseAllowance(
                              IERC20 token,
                              address spender,
                              uint256 value
                          ) internal {
                              uint256 newAllowance = token.allowance(address(this), spender) + value;
                              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                          }
                          function safeDecreaseAllowance(
                              IERC20 token,
                              address spender,
                              uint256 value
                          ) internal {
                              unchecked {
                                  uint256 oldAllowance = token.allowance(address(this), spender);
                                  require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                                  uint256 newAllowance = oldAllowance - value;
                                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                              }
                          }
                          function safePermit(
                              IERC20Permit token,
                              address owner,
                              address spender,
                              uint256 value,
                              uint256 deadline,
                              uint8 v,
                              bytes32 r,
                              bytes32 s
                          ) internal {
                              uint256 nonceBefore = token.nonces(owner);
                              token.permit(owner, spender, value, deadline, v, r, s);
                              uint256 nonceAfter = token.nonces(owner);
                              require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
                          }
                          /**
                           * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                           * on the return value: the return value is optional (but if data is returned, it must not be false).
                           * @param token The token targeted by the call.
                           * @param data The call data (encoded using abi.encode or one of its variants).
                           */
                          function _callOptionalReturn(IERC20 token, bytes memory data) private {
                              // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                              // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                              // the target address contains contract code and also asserts for success in the low-level call.
                              bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                              if (returndata.length > 0) {
                                  // Return data is optional
                                  require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                              }
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      // OpenZeppelin Contracts (last updated v4.8.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 functionCallWithValue(target, data, 0, "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");
                              (bool success, bytes memory returndata) = target.call{value: value}(data);
                              return verifyCallResultFromTarget(target, 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) {
                              (bool success, bytes memory returndata) = target.staticcall(data);
                              return verifyCallResultFromTarget(target, 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) {
                              (bool success, bytes memory returndata) = target.delegatecall(data);
                              return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                          }
                          /**
                           * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                           * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                           *
                           * _Available since v4.8._
                           */
                          function verifyCallResultFromTarget(
                              address target,
                              bool success,
                              bytes memory returndata,
                              string memory errorMessage
                          ) internal view returns (bytes memory) {
                              if (success) {
                                  if (returndata.length == 0) {
                                      // only check isContract if the call was successful and the return data is empty
                                      // otherwise we already know that it was a contract
                                      require(isContract(target), "Address: call to non-contract");
                                  }
                                  return returndata;
                              } else {
                                  _revert(returndata, errorMessage);
                              }
                          }
                          /**
                           * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                           * revert reason or 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 {
                                  _revert(returndata, errorMessage);
                              }
                          }
                          function _revert(bytes memory returndata, string memory errorMessage) private pure {
                              // 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);
                              }
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      // 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;
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)
                      pragma solidity ^0.8.0;
                      import "../Strings.sol";
                      /**
                       * @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 {
                          enum RecoverError {
                              NoError,
                              InvalidSignature,
                              InvalidSignatureLength,
                              InvalidSignatureS,
                              InvalidSignatureV // Deprecated in v4.8
                          }
                          function _throwError(RecoverError error) private pure {
                              if (error == RecoverError.NoError) {
                                  return; // no error: do nothing
                              } else if (error == RecoverError.InvalidSignature) {
                                  revert("ECDSA: invalid signature");
                              } else if (error == RecoverError.InvalidSignatureLength) {
                                  revert("ECDSA: invalid signature length");
                              } else if (error == RecoverError.InvalidSignatureS) {
                                  revert("ECDSA: invalid signature 's' value");
                              }
                          }
                          /**
                           * @dev Returns the address that signed a hashed message (`hash`) with
                           * `signature` or error string. 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.
                           *
                           * Documentation for signature generation:
                           * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
                           * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
                           *
                           * _Available since v4.3._
                           */
                          function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
                              if (signature.length == 65) {
                                  bytes32 r;
                                  bytes32 s;
                                  uint8 v;
                                  // ecrecover takes the signature parameters, and the only way to get them
                                  // currently is to use assembly.
                                  /// @solidity memory-safe-assembly
                                  assembly {
                                      r := mload(add(signature, 0x20))
                                      s := mload(add(signature, 0x40))
                                      v := byte(0, mload(add(signature, 0x60)))
                                  }
                                  return tryRecover(hash, v, r, s);
                              } else {
                                  return (address(0), RecoverError.InvalidSignatureLength);
                              }
                          }
                          /**
                           * @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) {
                              (address recovered, RecoverError error) = tryRecover(hash, signature);
                              _throwError(error);
                              return recovered;
                          }
                          /**
                           * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
                           *
                           * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
                           *
                           * _Available since v4.3._
                           */
                          function tryRecover(
                              bytes32 hash,
                              bytes32 r,
                              bytes32 vs
                          ) internal pure returns (address, RecoverError) {
                              bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                              uint8 v = uint8((uint256(vs) >> 255) + 27);
                              return tryRecover(hash, v, r, s);
                          }
                          /**
                           * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
                           *
                           * _Available since v4.2._
                           */
                          function recover(
                              bytes32 hash,
                              bytes32 r,
                              bytes32 vs
                          ) internal pure returns (address) {
                              (address recovered, RecoverError error) = tryRecover(hash, r, vs);
                              _throwError(error);
                              return recovered;
                          }
                          /**
                           * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
                           * `r` and `s` signature fields separately.
                           *
                           * _Available since v4.3._
                           */
                          function tryRecover(
                              bytes32 hash,
                              uint8 v,
                              bytes32 r,
                              bytes32 s
                          ) internal pure returns (address, RecoverError) {
                              // 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.
                              if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                                  return (address(0), RecoverError.InvalidSignatureS);
                              }
                              // If the signature is valid (and not malleable), return the signer address
                              address signer = ecrecover(hash, v, r, s);
                              if (signer == address(0)) {
                                  return (address(0), RecoverError.InvalidSignature);
                              }
                              return (signer, RecoverError.NoError);
                          }
                          /**
                           * @dev Overload of {ECDSA-recover} that receives the `v`,
                           * `r` and `s` signature fields separately.
                           */
                          function recover(
                              bytes32 hash,
                              uint8 v,
                              bytes32 r,
                              bytes32 s
                          ) internal pure returns (address) {
                              (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
                              _throwError(error);
                              return recovered;
                          }
                          /**
                           * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                           * produces hash corresponding to the one signed with the
                           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                           * JSON-RPC method as part of EIP-191.
                           *
                           * 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:\
                      32", hash));
                          }
                          /**
                           * @dev Returns an Ethereum Signed Message, created from `s`. This
                           * produces hash corresponding to the one signed with the
                           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                           * JSON-RPC method as part of EIP-191.
                           *
                           * See {recover}.
                           */
                          function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
                              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
                      ", Strings.toString(s.length), s));
                          }
                          /**
                           * @dev Returns an Ethereum Signed Typed Data, created from a
                           * `domainSeparator` and a `structHash`. This produces hash corresponding
                           * to the one signed with the
                           * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
                           * JSON-RPC method as part of EIP-712.
                           *
                           * See {recover}.
                           */
                          function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
                              return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
                      pragma solidity ^0.8.0;
                      /**
                       * @dev Standard math utilities missing in the Solidity language.
                       */
                      library Math {
                          enum Rounding {
                              Down, // Toward negative infinity
                              Up, // Toward infinity
                              Zero // Toward zero
                          }
                          /**
                           * @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 average of two numbers. The result is rounded towards
                           * zero.
                           */
                          function average(uint256 a, uint256 b) internal pure returns (uint256) {
                              // (a + b) / 2 can overflow.
                              return (a & b) + (a ^ b) / 2;
                          }
                          /**
                           * @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;
                          }
                          /**
                           * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                           * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                           * with further edits by Uniswap Labs also under MIT license.
                           */
                          function mulDiv(
                              uint256 x,
                              uint256 y,
                              uint256 denominator
                          ) internal pure returns (uint256 result) {
                              unchecked {
                                  // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                                  // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                                  // variables such that product = prod1 * 2^256 + prod0.
                                  uint256 prod0; // Least significant 256 bits of the product
                                  uint256 prod1; // Most significant 256 bits of the product
                                  assembly {
                                      let mm := mulmod(x, y, not(0))
                                      prod0 := mul(x, y)
                                      prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                                  }
                                  // Handle non-overflow cases, 256 by 256 division.
                                  if (prod1 == 0) {
                                      return prod0 / denominator;
                                  }
                                  // Make sure the result is less than 2^256. Also prevents denominator == 0.
                                  require(denominator > prod1);
                                  ///////////////////////////////////////////////
                                  // 512 by 256 division.
                                  ///////////////////////////////////////////////
                                  // Make division exact by subtracting the remainder from [prod1 prod0].
                                  uint256 remainder;
                                  assembly {
                                      // Compute remainder using mulmod.
                                      remainder := mulmod(x, y, denominator)
                                      // Subtract 256 bit number from 512 bit number.
                                      prod1 := sub(prod1, gt(remainder, prod0))
                                      prod0 := sub(prod0, remainder)
                                  }
                                  // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                                  // See https://cs.stackexchange.com/q/138556/92363.
                                  // Does not overflow because the denominator cannot be zero at this stage in the function.
                                  uint256 twos = denominator & (~denominator + 1);
                                  assembly {
                                      // Divide denominator by twos.
                                      denominator := div(denominator, twos)
                                      // Divide [prod1 prod0] by twos.
                                      prod0 := div(prod0, twos)
                                      // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                                      twos := add(div(sub(0, twos), twos), 1)
                                  }
                                  // Shift in bits from prod1 into prod0.
                                  prod0 |= prod1 * twos;
                                  // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                                  // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                                  // four bits. That is, denominator * inv = 1 mod 2^4.
                                  uint256 inverse = (3 * denominator) ^ 2;
                                  // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                                  // in modular arithmetic, doubling the correct bits in each step.
                                  inverse *= 2 - denominator * inverse; // inverse mod 2^8
                                  inverse *= 2 - denominator * inverse; // inverse mod 2^16
                                  inverse *= 2 - denominator * inverse; // inverse mod 2^32
                                  inverse *= 2 - denominator * inverse; // inverse mod 2^64
                                  inverse *= 2 - denominator * inverse; // inverse mod 2^128
                                  inverse *= 2 - denominator * inverse; // inverse mod 2^256
                                  // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                                  // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                                  // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                                  // is no longer required.
                                  result = prod0 * inverse;
                                  return result;
                              }
                          }
                          /**
                           * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                           */
                          function mulDiv(
                              uint256 x,
                              uint256 y,
                              uint256 denominator,
                              Rounding rounding
                          ) internal pure returns (uint256) {
                              uint256 result = mulDiv(x, y, denominator);
                              if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                                  result += 1;
                              }
                              return result;
                          }
                          /**
                           * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                           *
                           * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                           */
                          function sqrt(uint256 a) internal pure returns (uint256) {
                              if (a == 0) {
                                  return 0;
                              }
                              // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                              //
                              // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                              // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                              //
                              // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                              // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                              // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                              //
                              // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                              uint256 result = 1 << (log2(a) >> 1);
                              // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                              // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                              // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                              // into the expected uint128 result.
                              unchecked {
                                  result = (result + a / result) >> 1;
                                  result = (result + a / result) >> 1;
                                  result = (result + a / result) >> 1;
                                  result = (result + a / result) >> 1;
                                  result = (result + a / result) >> 1;
                                  result = (result + a / result) >> 1;
                                  result = (result + a / result) >> 1;
                                  return min(result, a / result);
                              }
                          }
                          /**
                           * @notice Calculates sqrt(a), following the selected rounding direction.
                           */
                          function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                              unchecked {
                                  uint256 result = sqrt(a);
                                  return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                              }
                          }
                          /**
                           * @dev Return the log in base 2, rounded down, of a positive value.
                           * Returns 0 if given 0.
                           */
                          function log2(uint256 value) internal pure returns (uint256) {
                              uint256 result = 0;
                              unchecked {
                                  if (value >> 128 > 0) {
                                      value >>= 128;
                                      result += 128;
                                  }
                                  if (value >> 64 > 0) {
                                      value >>= 64;
                                      result += 64;
                                  }
                                  if (value >> 32 > 0) {
                                      value >>= 32;
                                      result += 32;
                                  }
                                  if (value >> 16 > 0) {
                                      value >>= 16;
                                      result += 16;
                                  }
                                  if (value >> 8 > 0) {
                                      value >>= 8;
                                      result += 8;
                                  }
                                  if (value >> 4 > 0) {
                                      value >>= 4;
                                      result += 4;
                                  }
                                  if (value >> 2 > 0) {
                                      value >>= 2;
                                      result += 2;
                                  }
                                  if (value >> 1 > 0) {
                                      result += 1;
                                  }
                              }
                              return result;
                          }
                          /**
                           * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                           * Returns 0 if given 0.
                           */
                          function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                              unchecked {
                                  uint256 result = log2(value);
                                  return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                              }
                          }
                          /**
                           * @dev Return the log in base 10, rounded down, of a positive value.
                           * Returns 0 if given 0.
                           */
                          function log10(uint256 value) internal pure returns (uint256) {
                              uint256 result = 0;
                              unchecked {
                                  if (value >= 10**64) {
                                      value /= 10**64;
                                      result += 64;
                                  }
                                  if (value >= 10**32) {
                                      value /= 10**32;
                                      result += 32;
                                  }
                                  if (value >= 10**16) {
                                      value /= 10**16;
                                      result += 16;
                                  }
                                  if (value >= 10**8) {
                                      value /= 10**8;
                                      result += 8;
                                  }
                                  if (value >= 10**4) {
                                      value /= 10**4;
                                      result += 4;
                                  }
                                  if (value >= 10**2) {
                                      value /= 10**2;
                                      result += 2;
                                  }
                                  if (value >= 10**1) {
                                      result += 1;
                                  }
                              }
                              return result;
                          }
                          /**
                           * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                           * Returns 0 if given 0.
                           */
                          function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                              unchecked {
                                  uint256 result = log10(value);
                                  return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
                              }
                          }
                          /**
                           * @dev Return the log in base 256, rounded down, of a positive value.
                           * Returns 0 if given 0.
                           *
                           * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                           */
                          function log256(uint256 value) internal pure returns (uint256) {
                              uint256 result = 0;
                              unchecked {
                                  if (value >> 128 > 0) {
                                      value >>= 128;
                                      result += 16;
                                  }
                                  if (value >> 64 > 0) {
                                      value >>= 64;
                                      result += 8;
                                  }
                                  if (value >> 32 > 0) {
                                      value >>= 32;
                                      result += 4;
                                  }
                                  if (value >> 16 > 0) {
                                      value >>= 16;
                                      result += 2;
                                  }
                                  if (value >> 8 > 0) {
                                      result += 1;
                                  }
                              }
                              return result;
                          }
                          /**
                           * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                           * Returns 0 if given 0.
                           */
                          function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                              unchecked {
                                  uint256 result = log256(value);
                                  return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
                              }
                          }
                      }
                      // SPDX-License-Identifier: MIT
                      // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
                      pragma solidity ^0.8.0;
                      import "./math/Math.sol";
                      /**
                       * @dev String operations.
                       */
                      library Strings {
                          bytes16 private constant _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) {
                              unchecked {
                                  uint256 length = Math.log10(value) + 1;
                                  string memory buffer = new string(length);
                                  uint256 ptr;
                                  /// @solidity memory-safe-assembly
                                  assembly {
                                      ptr := add(buffer, add(32, length))
                                  }
                                  while (true) {
                                      ptr--;
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                                      }
                                      value /= 10;
                                      if (value == 0) break;
                                  }
                                  return buffer;
                              }
                          }
                          /**
                           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                           */
                          function toHexString(uint256 value) internal pure returns (string memory) {
                              unchecked {
                                  return toHexString(value, Math.log256(value) + 1);
                              }
                          }
                          /**
                           * @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] = _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);
                          }
                      }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      /* solhint-disable reason-string */
                      import "@openzeppelin/contracts/access/Ownable.sol";
                      import "../interfaces/IPaymaster.sol";
                      import "../interfaces/IEntryPoint.sol";
                      import "./Helpers.sol";
                      /**
                       * Helper class for creating a paymaster.
                       * provides helper methods for staking.
                       * validates that the postOp is called only by the entryPoint
                       */
                      abstract contract BasePaymaster is IPaymaster, Ownable {
                          IEntryPoint immutable public entryPoint;
                          constructor(IEntryPoint _entryPoint) {
                              entryPoint = _entryPoint;
                          }
                          /// @inheritdoc IPaymaster
                          function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost)
                          external override returns (bytes memory context, uint256 validationData) {
                               _requireFromEntryPoint();
                              return _validatePaymasterUserOp(userOp, userOpHash, maxCost);
                          }
                          function _validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost)
                          internal virtual returns (bytes memory context, uint256 validationData);
                          /// @inheritdoc IPaymaster
                          function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external override {
                              _requireFromEntryPoint();
                              _postOp(mode, context, actualGasCost);
                          }
                          /**
                           * post-operation handler.
                           * (verified to be called only through the entryPoint)
                           * @dev if subclass returns a non-empty context from validatePaymasterUserOp, it must also implement this method.
                           * @param mode enum with the following options:
                           *      opSucceeded - user operation succeeded.
                           *      opReverted  - user op reverted. still has to pay for gas.
                           *      postOpReverted - user op succeeded, but caused postOp (in mode=opSucceeded) to revert.
                           *                       Now this is the 2nd call, after user's op was deliberately reverted.
                           * @param context - the context value returned by validatePaymasterUserOp
                           * @param actualGasCost - actual gas used so far (without this postOp call).
                           */
                          function _postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) internal virtual {
                              (mode,context,actualGasCost); // unused params
                              // subclass must override this method if validatePaymasterUserOp returns a context
                              revert("must override");
                          }
                          /**
                           * add a deposit for this paymaster, used for paying for transaction fees
                           */
                          function deposit() public payable {
                              entryPoint.depositTo{value : msg.value}(address(this));
                          }
                          /**
                           * withdraw value from the deposit
                           * @param withdrawAddress target to send to
                           * @param amount to withdraw
                           */
                          function withdrawTo(address payable withdrawAddress, uint256 amount) public onlyOwner {
                              entryPoint.withdrawTo(withdrawAddress, amount);
                          }
                          /**
                           * add stake for this paymaster.
                           * This method can also carry eth value to add to the current stake.
                           * @param unstakeDelaySec - the unstake delay for this paymaster. Can only be increased.
                           */
                          function addStake(uint32 unstakeDelaySec) external payable onlyOwner {
                              entryPoint.addStake{value : msg.value}(unstakeDelaySec);
                          }
                          /**
                           * return current paymaster's deposit on the entryPoint.
                           */
                          function getDeposit() public view returns (uint256) {
                              return entryPoint.balanceOf(address(this));
                          }
                          /**
                           * unlock the stake, in order to withdraw it.
                           * The paymaster can't serve requests once unlocked, until it calls addStake again
                           */
                          function unlockStake() external onlyOwner {
                              entryPoint.unlockStake();
                          }
                          /**
                           * withdraw the entire paymaster's stake.
                           * stake must be unlocked first (and then wait for the unstakeDelay to be over)
                           * @param withdrawAddress the address to send withdrawn value.
                           */
                          function withdrawStake(address payable withdrawAddress) external onlyOwner {
                              entryPoint.withdrawStake(withdrawAddress);
                          }
                          /// validate the call is made from a valid entrypoint
                          function _requireFromEntryPoint() internal virtual {
                              require(msg.sender == address(entryPoint), "Sender not EntryPoint");
                          }
                      }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      /* solhint-disable no-inline-assembly */
                      /**
                       * returned data from validateUserOp.
                       * validateUserOp returns a uint256, with is created by `_packedValidationData` and parsed by `_parseValidationData`
                       * @param aggregator - address(0) - the account validated the signature by itself.
                       *              address(1) - the account failed to validate the signature.
                       *              otherwise - this is an address of a signature aggregator that must be used to validate the signature.
                       * @param validAfter - this UserOp is valid only after this timestamp.
                       * @param validaUntil - this UserOp is valid only up to this timestamp.
                       */
                          struct ValidationData {
                              address aggregator;
                              uint48 validAfter;
                              uint48 validUntil;
                          }
                      //extract sigFailed, validAfter, validUntil.
                      // also convert zero validUntil to type(uint48).max
                          function _parseValidationData(uint validationData) pure returns (ValidationData memory data) {
                              address aggregator = address(uint160(validationData));
                              uint48 validUntil = uint48(validationData >> 160);
                              if (validUntil == 0) {
                                  validUntil = type(uint48).max;
                              }
                              uint48 validAfter = uint48(validationData >> (48 + 160));
                              return ValidationData(aggregator, validAfter, validUntil);
                          }
                      // intersect account and paymaster ranges.
                          function _intersectTimeRange(uint256 validationData, uint256 paymasterValidationData) pure returns (ValidationData memory) {
                              ValidationData memory accountValidationData = _parseValidationData(validationData);
                              ValidationData memory pmValidationData = _parseValidationData(paymasterValidationData);
                              address aggregator = accountValidationData.aggregator;
                              if (aggregator == address(0)) {
                                  aggregator = pmValidationData.aggregator;
                              }
                              uint48 validAfter = accountValidationData.validAfter;
                              uint48 validUntil = accountValidationData.validUntil;
                              uint48 pmValidAfter = pmValidationData.validAfter;
                              uint48 pmValidUntil = pmValidationData.validUntil;
                              if (validAfter < pmValidAfter) validAfter = pmValidAfter;
                              if (validUntil > pmValidUntil) validUntil = pmValidUntil;
                              return ValidationData(aggregator, validAfter, validUntil);
                          }
                      /**
                       * helper to pack the return value for validateUserOp
                       * @param data - the ValidationData to pack
                       */
                          function _packValidationData(ValidationData memory data) pure returns (uint256) {
                              return uint160(data.aggregator) | (uint256(data.validUntil) << 160) | (uint256(data.validAfter) << (160 + 48));
                          }
                      /**
                       * helper to pack the return value for validateUserOp, when not using an aggregator
                       * @param sigFailed - true for signature failure, false for success
                       * @param validUntil last timestamp this UserOperation is valid (or zero for infinite)
                       * @param validAfter first timestamp this UserOperation is valid
                       */
                          function _packValidationData(bool sigFailed, uint48 validUntil, uint48 validAfter) pure returns (uint256) {
                              return (sigFailed ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48));
                          }
                      /**
                       * keccak function over calldata.
                       * @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it.
                       */
                          function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) {
                              assembly {
                                  let mem := mload(0x40)
                                  let len := data.length
                                  calldatacopy(mem, data.offset, len)
                                  ret := keccak256(mem, len)
                              }
                          }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      import "./UserOperation.sol";
                      /**
                       * Aggregated Signatures validator.
                       */
                      interface IAggregator {
                          /**
                           * validate aggregated signature.
                           * revert if the aggregated signature does not match the given list of operations.
                           */
                          function validateSignatures(UserOperation[] calldata userOps, bytes calldata signature) external view;
                          /**
                           * validate signature of a single userOp
                           * This method is should be called by bundler after EntryPoint.simulateValidation() returns (reverts) with ValidationResultWithAggregation
                           * First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps.
                           * @param userOp the userOperation received from the user.
                           * @return sigForUserOp the value to put into the signature field of the userOp when calling handleOps.
                           *    (usually empty, unless account and aggregator support some kind of "multisig"
                           */
                          function validateUserOpSignature(UserOperation calldata userOp)
                          external view returns (bytes memory sigForUserOp);
                          /**
                           * aggregate multiple signatures into a single value.
                           * This method is called off-chain to calculate the signature to pass with handleOps()
                           * bundler MAY use optimized custom code perform this aggregation
                           * @param userOps array of UserOperations to collect the signatures from.
                           * @return aggregatedSignature the aggregated signature
                           */
                          function aggregateSignatures(UserOperation[] calldata userOps) external view returns (bytes memory aggregatedSignature);
                      }
                      /**
                       ** Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
                       ** Only one instance required on each chain.
                       **/
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      /* solhint-disable avoid-low-level-calls */
                      /* solhint-disable no-inline-assembly */
                      /* solhint-disable reason-string */
                      import "./UserOperation.sol";
                      import "./IStakeManager.sol";
                      import "./IAggregator.sol";
                      import "./INonceManager.sol";
                      interface IEntryPoint is IStakeManager, INonceManager {
                          /***
                           * An event emitted after each successful request
                           * @param userOpHash - unique identifier for the request (hash its entire content, except signature).
                           * @param sender - the account that generates this request.
                           * @param paymaster - if non-null, the paymaster that pays for this request.
                           * @param nonce - the nonce value from the request.
                           * @param success - true if the sender transaction succeeded, false if reverted.
                           * @param actualGasCost - actual amount paid (by account or paymaster) for this UserOperation.
                           * @param actualGasUsed - total gas used by this UserOperation (including preVerification, creation, validation and execution).
                           */
                          event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed);
                          /**
                           * account "sender" was deployed.
                           * @param userOpHash the userOp that deployed this account. UserOperationEvent will follow.
                           * @param sender the account that is deployed
                           * @param factory the factory used to deploy this account (in the initCode)
                           * @param paymaster the paymaster used by this UserOp
                           */
                          event AccountDeployed(bytes32 indexed userOpHash, address indexed sender, address factory, address paymaster);
                          /**
                           * An event emitted if the UserOperation "callData" reverted with non-zero length
                           * @param userOpHash the request unique identifier.
                           * @param sender the sender of this request
                           * @param nonce the nonce used in the request
                           * @param revertReason - the return bytes from the (reverted) call to "callData".
                           */
                          event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason);
                          /**
                           * an event emitted by handleOps(), before starting the execution loop.
                           * any event emitted before this event, is part of the validation.
                           */
                          event BeforeExecution();
                          /**
                           * signature aggregator used by the following UserOperationEvents within this bundle.
                           */
                          event SignatureAggregatorChanged(address indexed aggregator);
                          /**
                           * a custom revert error of handleOps, to identify the offending op.
                           *  NOTE: if simulateValidation passes successfully, there should be no reason for handleOps to fail on it.
                           *  @param opIndex - index into the array of ops to the failed one (in simulateValidation, this is always zero)
                           *  @param reason - revert reason
                           *      The string starts with a unique code "AAmn", where "m" is "1" for factory, "2" for account and "3" for paymaster issues,
                           *      so a failure can be attributed to the correct entity.
                           *   Should be caught in off-chain handleOps simulation and not happen on-chain.
                           *   Useful for mitigating DoS attempts against batchers or for troubleshooting of factory/account/paymaster reverts.
                           */
                          error FailedOp(uint256 opIndex, string reason);
                          /**
                           * error case when a signature aggregator fails to verify the aggregated signature it had created.
                           */
                          error SignatureValidationFailed(address aggregator);
                          /**
                           * Successful result from simulateValidation.
                           * @param returnInfo gas and time-range returned values
                           * @param senderInfo stake information about the sender
                           * @param factoryInfo stake information about the factory (if any)
                           * @param paymasterInfo stake information about the paymaster (if any)
                           */
                          error ValidationResult(ReturnInfo returnInfo,
                              StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo);
                          /**
                           * Successful result from simulateValidation, if the account returns a signature aggregator
                           * @param returnInfo gas and time-range returned values
                           * @param senderInfo stake information about the sender
                           * @param factoryInfo stake information about the factory (if any)
                           * @param paymasterInfo stake information about the paymaster (if any)
                           * @param aggregatorInfo signature aggregation info (if the account requires signature aggregator)
                           *      bundler MUST use it to verify the signature, or reject the UserOperation
                           */
                          error ValidationResultWithAggregation(ReturnInfo returnInfo,
                              StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo,
                              AggregatorStakeInfo aggregatorInfo);
                          /**
                           * return value of getSenderAddress
                           */
                          error SenderAddressResult(address sender);
                          /**
                           * return value of simulateHandleOp
                           */
                          error ExecutionResult(uint256 preOpGas, uint256 paid, uint48 validAfter, uint48 validUntil, bool targetSuccess, bytes targetResult);
                          //UserOps handled, per aggregator
                          struct UserOpsPerAggregator {
                              UserOperation[] userOps;
                              // aggregator address
                              IAggregator aggregator;
                              // aggregated signature
                              bytes signature;
                          }
                          /**
                           * Execute a batch of UserOperation.
                           * no signature aggregator is used.
                           * if any account requires an aggregator (that is, it returned an aggregator when
                           * performing simulateValidation), then handleAggregatedOps() must be used instead.
                           * @param ops the operations to execute
                           * @param beneficiary the address to receive the fees
                           */
                          function handleOps(UserOperation[] calldata ops, address payable beneficiary) external;
                          /**
                           * Execute a batch of UserOperation with Aggregators
                           * @param opsPerAggregator the operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts)
                           * @param beneficiary the address to receive the fees
                           */
                          function handleAggregatedOps(
                              UserOpsPerAggregator[] calldata opsPerAggregator,
                              address payable beneficiary
                          ) external;
                          /**
                           * generate a request Id - unique identifier for this request.
                           * the request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid.
                           */
                          function getUserOpHash(UserOperation calldata userOp) external view returns (bytes32);
                          /**
                           * Simulate a call to account.validateUserOp and paymaster.validatePaymasterUserOp.
                           * @dev this method always revert. Successful result is ValidationResult error. other errors are failures.
                           * @dev The node must also verify it doesn't use banned opcodes, and that it doesn't reference storage outside the account's data.
                           * @param userOp the user operation to validate.
                           */
                          function simulateValidation(UserOperation calldata userOp) external;
                          /**
                           * gas and return values during simulation
                           * @param preOpGas the gas used for validation (including preValidationGas)
                           * @param prefund the required prefund for this operation
                           * @param sigFailed validateUserOp's (or paymaster's) signature check failed
                           * @param validAfter - first timestamp this UserOp is valid (merging account and paymaster time-range)
                           * @param validUntil - last timestamp this UserOp is valid (merging account and paymaster time-range)
                           * @param paymasterContext returned by validatePaymasterUserOp (to be passed into postOp)
                           */
                          struct ReturnInfo {
                              uint256 preOpGas;
                              uint256 prefund;
                              bool sigFailed;
                              uint48 validAfter;
                              uint48 validUntil;
                              bytes paymasterContext;
                          }
                          /**
                           * returned aggregated signature info.
                           * the aggregator returned by the account, and its current stake.
                           */
                          struct AggregatorStakeInfo {
                              address aggregator;
                              StakeInfo stakeInfo;
                          }
                          /**
                           * Get counterfactual sender address.
                           *  Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation.
                           * this method always revert, and returns the address in SenderAddressResult error
                           * @param initCode the constructor code to be passed into the UserOperation.
                           */
                          function getSenderAddress(bytes memory initCode) external;
                          /**
                           * simulate full execution of a UserOperation (including both validation and target execution)
                           * this method will always revert with "ExecutionResult".
                           * it performs full validation of the UserOperation, but ignores signature error.
                           * an optional target address is called after the userop succeeds, and its value is returned
                           * (before the entire call is reverted)
                           * Note that in order to collect the the success/failure of the target call, it must be executed
                           * with trace enabled to track the emitted events.
                           * @param op the UserOperation to simulate
                           * @param target if nonzero, a target address to call after userop simulation. If called, the targetSuccess and targetResult
                           *        are set to the return from that call.
                           * @param targetCallData callData to pass to target address
                           */
                          function simulateHandleOp(UserOperation calldata op, address target, bytes calldata targetCallData) external;
                      }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      interface INonceManager {
                          /**
                           * Return the next nonce for this sender.
                           * Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop)
                           * But UserOp with different keys can come with arbitrary order.
                           *
                           * @param sender the account address
                           * @param key the high 192 bit of the nonce
                           * @return nonce a full nonce to pass for next UserOp with this sender.
                           */
                          function getNonce(address sender, uint192 key)
                          external view returns (uint256 nonce);
                          /**
                           * Manually increment the nonce of the sender.
                           * This method is exposed just for completeness..
                           * Account does NOT need to call it, neither during validation, nor elsewhere,
                           * as the EntryPoint will update the nonce regardless.
                           * Possible use-case is call it with various keys to "initialize" their nonces to one, so that future
                           * UserOperations will not pay extra for the first transaction with a given key.
                           */
                          function incrementNonce(uint192 key) external;
                      }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      import "./UserOperation.sol";
                      /**
                       * the interface exposed by a paymaster contract, who agrees to pay the gas for user's operations.
                       * a paymaster must hold a stake to cover the required entrypoint stake and also the gas for the transaction.
                       */
                      interface IPaymaster {
                          enum PostOpMode {
                              opSucceeded, // user op succeeded
                              opReverted, // user op reverted. still has to pay for gas.
                              postOpReverted //user op succeeded, but caused postOp to revert. Now it's a 2nd call, after user's op was deliberately reverted.
                          }
                          /**
                           * payment validation: check if paymaster agrees to pay.
                           * Must verify sender is the entryPoint.
                           * Revert to reject this request.
                           * Note that bundlers will reject this method if it changes the state, unless the paymaster is trusted (whitelisted)
                           * The paymaster pre-pays using its deposit, and receive back a refund after the postOp method returns.
                           * @param userOp the user operation
                           * @param userOpHash hash of the user's request data.
                           * @param maxCost the maximum cost of this transaction (based on maximum gas and gas price from userOp)
                           * @return context value to send to a postOp
                           *      zero length to signify postOp is not required.
                           * @return validationData signature and time-range of this operation, encoded the same as the return value of validateUserOperation
                           *      <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
                           *         otherwise, an address of an "authorizer" contract.
                           *      <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite"
                           *      <6-byte> validAfter - first timestamp this operation is valid
                           *      Note that the validation code cannot use block.timestamp (or block.number) directly.
                           */
                          function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost)
                          external returns (bytes memory context, uint256 validationData);
                          /**
                           * post-operation handler.
                           * Must verify sender is the entryPoint
                           * @param mode enum with the following options:
                           *      opSucceeded - user operation succeeded.
                           *      opReverted  - user op reverted. still has to pay for gas.
                           *      postOpReverted - user op succeeded, but caused postOp (in mode=opSucceeded) to revert.
                           *                       Now this is the 2nd call, after user's op was deliberately reverted.
                           * @param context - the context value returned by validatePaymasterUserOp
                           * @param actualGasCost - actual gas used so far (without this postOp call).
                           */
                          function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external;
                      }
                      // SPDX-License-Identifier: GPL-3.0-only
                      pragma solidity ^0.8.12;
                      /**
                       * manage deposits and stakes.
                       * deposit is just a balance used to pay for UserOperations (either by a paymaster or an account)
                       * stake is value locked for at least "unstakeDelay" by the staked entity.
                       */
                      interface IStakeManager {
                          event Deposited(
                              address indexed account,
                              uint256 totalDeposit
                          );
                          event Withdrawn(
                              address indexed account,
                              address withdrawAddress,
                              uint256 amount
                          );
                          /// Emitted when stake or unstake delay are modified
                          event StakeLocked(
                              address indexed account,
                              uint256 totalStaked,
                              uint256 unstakeDelaySec
                          );
                          /// Emitted once a stake is scheduled for withdrawal
                          event StakeUnlocked(
                              address indexed account,
                              uint256 withdrawTime
                          );
                          event StakeWithdrawn(
                              address indexed account,
                              address withdrawAddress,
                              uint256 amount
                          );
                          /**
                           * @param deposit the entity's deposit
                           * @param staked true if this entity is staked.
                           * @param stake actual amount of ether staked for this entity.
                           * @param unstakeDelaySec minimum delay to withdraw the stake.
                           * @param withdrawTime - first block timestamp where 'withdrawStake' will be callable, or zero if already locked
                           * @dev sizes were chosen so that (deposit,staked, stake) fit into one cell (used during handleOps)
                           *    and the rest fit into a 2nd cell.
                           *    112 bit allows for 10^15 eth
                           *    48 bit for full timestamp
                           *    32 bit allows 150 years for unstake delay
                           */
                          struct DepositInfo {
                              uint112 deposit;
                              bool staked;
                              uint112 stake;
                              uint32 unstakeDelaySec;
                              uint48 withdrawTime;
                          }
                          //API struct used by getStakeInfo and simulateValidation
                          struct StakeInfo {
                              uint256 stake;
                              uint256 unstakeDelaySec;
                          }
                          /// @return info - full deposit information of given account
                          function getDepositInfo(address account) external view returns (DepositInfo memory info);
                          /// @return the deposit (for gas payment) of the account
                          function balanceOf(address account) external view returns (uint256);
                          /**
                           * add to the deposit of the given account
                           */
                          function depositTo(address account) external payable;
                          /**
                           * add to the account's stake - amount and delay
                           * any pending unstake is first cancelled.
                           * @param _unstakeDelaySec the new lock duration before the deposit can be withdrawn.
                           */
                          function addStake(uint32 _unstakeDelaySec) external payable;
                          /**
                           * attempt to unlock the stake.
                           * the value can be withdrawn (using withdrawStake) after the unstake delay.
                           */
                          function unlockStake() external;
                          /**
                           * withdraw from the (unlocked) stake.
                           * must first call unlockStake and wait for the unstakeDelay to pass
                           * @param withdrawAddress the address to send withdrawn value.
                           */
                          function withdrawStake(address payable withdrawAddress) external;
                          /**
                           * withdraw from the deposit.
                           * @param withdrawAddress the address to send withdrawn value.
                           * @param withdrawAmount the amount to withdraw.
                           */
                          function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external;
                      }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.12;
                      /* solhint-disable no-inline-assembly */
                      import {calldataKeccak} from "../core/Helpers.sol";
                      /**
                       * User Operation struct
                       * @param sender the sender account of this request.
                           * @param nonce unique value the sender uses to verify it is not a replay.
                           * @param initCode if set, the account contract will be created by this constructor/
                           * @param callData the method call to execute on this account.
                           * @param callGasLimit the gas limit passed to the callData method call.
                           * @param verificationGasLimit gas used for validateUserOp and validatePaymasterUserOp.
                           * @param preVerificationGas gas not calculated by the handleOps method, but added to the gas paid. Covers batch overhead.
                           * @param maxFeePerGas same as EIP-1559 gas parameter.
                           * @param maxPriorityFeePerGas same as EIP-1559 gas parameter.
                           * @param paymasterAndData if set, this field holds the paymaster address and paymaster-specific data. the paymaster will pay for the transaction instead of the sender.
                           * @param signature sender-verified signature over the entire request, the EntryPoint address and the chain ID.
                           */
                          struct UserOperation {
                              address sender;
                              uint256 nonce;
                              bytes initCode;
                              bytes callData;
                              uint256 callGasLimit;
                              uint256 verificationGasLimit;
                              uint256 preVerificationGas;
                              uint256 maxFeePerGas;
                              uint256 maxPriorityFeePerGas;
                              bytes paymasterAndData;
                              bytes signature;
                          }
                      /**
                       * Utility functions helpful when working with UserOperation structs.
                       */
                      library UserOperationLib {
                          function getSender(UserOperation calldata userOp) internal pure returns (address) {
                              address data;
                              //read sender from userOp, which is first userOp member (saves 800 gas...)
                              assembly {data := calldataload(userOp)}
                              return address(uint160(data));
                          }
                          //relayer/block builder might submit the TX with higher priorityFee, but the user should not
                          // pay above what he signed for.
                          function gasPrice(UserOperation calldata userOp) internal view returns (uint256) {
                          unchecked {
                              uint256 maxFeePerGas = userOp.maxFeePerGas;
                              uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                              if (maxFeePerGas == maxPriorityFeePerGas) {
                                  //legacy mode (for networks that don't support basefee opcode)
                                  return maxFeePerGas;
                              }
                              return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
                          }
                          }
                          function pack(UserOperation calldata userOp) internal pure returns (bytes memory ret) {
                              address sender = getSender(userOp);
                              uint256 nonce = userOp.nonce;
                              bytes32 hashInitCode = calldataKeccak(userOp.initCode);
                              bytes32 hashCallData = calldataKeccak(userOp.callData);
                              uint256 callGasLimit = userOp.callGasLimit;
                              uint256 verificationGasLimit = userOp.verificationGasLimit;
                              uint256 preVerificationGas = userOp.preVerificationGas;
                              uint256 maxFeePerGas = userOp.maxFeePerGas;
                              uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
                              bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);
                              return abi.encode(
                                  sender, nonce,
                                  hashInitCode, hashCallData,
                                  callGasLimit, verificationGasLimit, preVerificationGas,
                                  maxFeePerGas, maxPriorityFeePerGas,
                                  hashPaymasterAndData
                              );
                          }
                          function hash(UserOperation calldata userOp) internal pure returns (bytes32) {
                              return keccak256(pack(userOp));
                          }
                          function min(uint256 a, uint256 b) internal pure returns (uint256) {
                              return a < b ? a : b;
                          }
                      }
                      // SPDX-License-Identifier: GPL-3.0
                      pragma solidity ^0.8.19;
                      import { IEntryPoint } from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
                      import { UserOperation } from "account-abstraction/contracts/interfaces/UserOperation.sol";
                      import { UserOperationLib } from "account-abstraction/contracts/interfaces/UserOperation.sol";
                      import { BasePaymaster } from "account-abstraction/contracts/core/BasePaymaster.sol";
                      import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
                      import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                      import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                      import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
                      import "account-abstraction/contracts/core/Helpers.sol" as Helpers;
                      /**
                       * A paymaster based on the eth-infinitism sample VerifyingPaymaster contract.
                       * It has the same functionality as the sample, but with added support for withdrawing ERC20 tokens.
                       * All withdrawn tokens will be transferred to the owner address.
                       * Note that the off-chain signer should have a strategy in place to handle a failed token withdrawal.
                       *
                       * See account-abstraction/contracts/samples/VerifyingPaymaster.sol for detailed comments.
                       */
                      contract VerifyingPaymaster is BasePaymaster {
                          using ECDSA for bytes32;
                          using UserOperationLib for UserOperation;
                          using SafeERC20 for IERC20;
                          mapping(address sender => uint256 nonce) public senderNonce;
                          uint256 private constant VALID_PND_OFFSET = 20;
                          uint256 private constant SIGNATURE_OFFSET = 148;
                          uint256 public constant POST_OP_GAS = 35000;
                          constructor(IEntryPoint _entryPoint, address _owner) BasePaymaster(_entryPoint) {
                              _transferOwnership(_owner);
                          }
                          function pack(UserOperation calldata userOp) internal pure returns (bytes memory ret) {
                              bytes calldata pnd = userOp.paymasterAndData;
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  let ofs := userOp
                                  let len := sub(sub(pnd.offset, ofs), 32)
                                  ret := mload(0x40)
                                  mstore(0x40, add(ret, add(len, 32)))
                                  mstore(ret, len)
                                  calldatacopy(add(ret, 32), ofs, len)
                              }
                          }
                          function getHash(
                              UserOperation calldata userOp,
                              uint48 validUntil,
                              uint48 validAfter,
                              address erc20Token,
                              uint256 exchangeRate
                          ) public view returns (bytes32) {
                              return
                                  keccak256(
                                      abi.encode(
                                          pack(userOp),
                                          block.chainid,
                                          address(this),
                                          senderNonce[userOp.getSender()],
                                          validUntil,
                                          validAfter,
                                          erc20Token,
                                          exchangeRate
                                      )
                                  );
                          }
                          function _validatePaymasterUserOp(
                              UserOperation calldata userOp,
                              bytes32 /*userOpHash*/,
                              uint256 requiredPreFund
                          ) internal override returns (bytes memory context, uint256 validationData) {
                              (requiredPreFund);
                              (
                                  uint48 validUntil,
                                  uint48 validAfter,
                                  address erc20Token,
                                  uint256 exchangeRate,
                                  bytes calldata signature
                              ) = parsePaymasterAndData(userOp.paymasterAndData);
                              // solhint-disable-next-line reason-string
                              require(
                                  signature.length == 64 || signature.length == 65,
                                  "VerifyingPaymaster: invalid signature length in paymasterAndData"
                              );
                              bytes32 hash = ECDSA.toEthSignedMessageHash(getHash(userOp, validUntil, validAfter, erc20Token, exchangeRate));
                              senderNonce[userOp.getSender()]++;
                              context = "";
                              if (erc20Token != address(0)) {
                                  context = abi.encode(
                                      userOp.sender,
                                      erc20Token,
                                      exchangeRate,
                                      userOp.maxFeePerGas,
                                      userOp.maxPriorityFeePerGas
                                  );
                              }
                              if (owner() != ECDSA.recover(hash, signature)) {
                                  return (context, Helpers._packValidationData(true, validUntil, validAfter));
                              }
                              return (context, Helpers._packValidationData(false, validUntil, validAfter));
                          }
                          function _postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) internal override {
                              (address sender, IERC20 token, uint256 exchangeRate, uint256 maxFeePerGas, uint256 maxPriorityFeePerGas) = abi
                                  .decode(context, (address, IERC20, uint256, uint256, uint256));
                              uint256 opGasPrice;
                              unchecked {
                                  if (maxFeePerGas == maxPriorityFeePerGas) {
                                      opGasPrice = maxFeePerGas;
                                  } else {
                                      opGasPrice = Math.min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
                                  }
                              }
                              uint256 actualTokenCost = ((actualGasCost + (POST_OP_GAS * opGasPrice)) * exchangeRate) / 1e18;
                              if (mode != PostOpMode.postOpReverted) {
                                  token.safeTransferFrom(sender, owner(), actualTokenCost);
                              }
                          }
                          function parsePaymasterAndData(
                              bytes calldata paymasterAndData
                          )
                              public
                              pure
                              returns (
                                  uint48 validUntil,
                                  uint48 validAfter,
                                  address erc20Token,
                                  uint256 exchangeRate,
                                  bytes calldata signature
                              )
                          {
                              (validUntil, validAfter, erc20Token, exchangeRate) = abi.decode(
                                  paymasterAndData[VALID_PND_OFFSET:SIGNATURE_OFFSET],
                                  (uint48, uint48, address, uint256)
                              );
                              signature = paymasterAndData[SIGNATURE_OFFSET:];
                          }
                      }
                      

                      File 9 of 12: MultiSend
                      // SPDX-License-Identifier: LGPL-3.0-only
                      pragma solidity >=0.7.0 <0.9.0;
                      /// @title Multi Send - Allows to batch multiple transactions into one.
                      /// @author Nick Dodson - <nick.dodson@consensys.net>
                      /// @author Gonçalo Sá - <goncalo.sa@consensys.net>
                      /// @author Stefan George - <stefan@gnosis.io>
                      /// @author Richard Meissner - <richard@gnosis.io>
                      contract MultiSend {
                          address private immutable multisendSingleton;
                          constructor() {
                              multisendSingleton = address(this);
                          }
                          /// @dev Sends multiple transactions and reverts all if one fails.
                          /// @param transactions Encoded transactions. Each transaction is encoded as a packed bytes of
                          ///                     operation as a uint8 with 0 for a call or 1 for a delegatecall (=> 1 byte),
                          ///                     to as a address (=> 20 bytes),
                          ///                     value as a uint256 (=> 32 bytes),
                          ///                     data length as a uint256 (=> 32 bytes),
                          ///                     data as bytes.
                          ///                     see abi.encodePacked for more information on packed encoding
                          /// @notice This method is payable as delegatecalls keep the msg.value from the previous call
                          ///         If the calling method (e.g. execTransaction) received ETH this would revert otherwise
                          function multiSend(bytes memory transactions) public payable {
                              require(address(this) != multisendSingleton, "MultiSend should only be called via delegatecall");
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  let length := mload(transactions)
                                  let i := 0x20
                                  for {
                                      // Pre block is not used in "while mode"
                                  } lt(i, length) {
                                      // Post block is not used in "while mode"
                                  } {
                                      // First byte of the data is the operation.
                                      // We shift by 248 bits (256 - 8 [operation byte]) it right since mload will always load 32 bytes (a word).
                                      // This will also zero out unused data.
                                      let operation := shr(0xf8, mload(add(transactions, i)))
                                      // We offset the load address by 1 byte (operation byte)
                                      // We shift it right by 96 bits (256 - 160 [20 address bytes]) to right-align the data and zero out unused data.
                                      let to := shr(0x60, mload(add(transactions, add(i, 0x01))))
                                      // We offset the load address by 21 byte (operation byte + 20 address bytes)
                                      let value := mload(add(transactions, add(i, 0x15)))
                                      // We offset the load address by 53 byte (operation byte + 20 address bytes + 32 value bytes)
                                      let dataLength := mload(add(transactions, add(i, 0x35)))
                                      // We offset the load address by 85 byte (operation byte + 20 address bytes + 32 value bytes + 32 data length bytes)
                                      let data := add(transactions, add(i, 0x55))
                                      let success := 0
                                      switch operation
                                          case 0 {
                                              success := call(gas(), to, value, data, dataLength, 0, 0)
                                          }
                                          case 1 {
                                              success := delegatecall(gas(), to, data, dataLength, 0, 0)
                                          }
                                      if eq(success, 0) {
                                          revert(0, 0)
                                      }
                                      // Next entry starts at 85 byte + data length
                                      i := add(i, add(0x55, dataLength))
                                  }
                              }
                          }
                      }

                      File 10 of 12: 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 11 of 12: 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 <info@lido.fi>
                      // 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 <info@lido.fi>
                      // 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 <info@lido.fi>
                      // 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 <info@lido.fi>
                      // 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 <info@lido.fi>
                      // 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 <info@lido.fi>
                      // 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 <info@lido.fi>
                      // 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 <info@lido.fi>
                      // 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 <info@lido.fi>
                      // 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 <info@lido.fi>
                      // 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 <info@lido.fi>
                      // 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
                        );
                      }
                      

                      File 12 of 12: ZeroXSwapImpl
                      // SPDX-License-Identifier: MIT
                      pragma solidity >=0.8.0;
                      
                      /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
                      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
                      /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
                      /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
                      abstract contract ERC20 {
                          /*//////////////////////////////////////////////////////////////
                                                       EVENTS
                          //////////////////////////////////////////////////////////////*/
                      
                          event Transfer(address indexed from, address indexed to, uint256 amount);
                      
                          event Approval(address indexed owner, address indexed spender, uint256 amount);
                      
                          /*//////////////////////////////////////////////////////////////
                                                  METADATA STORAGE
                          //////////////////////////////////////////////////////////////*/
                      
                          string public name;
                      
                          string public symbol;
                      
                          uint8 public immutable decimals;
                      
                          /*//////////////////////////////////////////////////////////////
                                                    ERC20 STORAGE
                          //////////////////////////////////////////////////////////////*/
                      
                          uint256 public totalSupply;
                      
                          mapping(address => uint256) public balanceOf;
                      
                          mapping(address => mapping(address => uint256)) public allowance;
                      
                          /*//////////////////////////////////////////////////////////////
                                                  EIP-2612 STORAGE
                          //////////////////////////////////////////////////////////////*/
                      
                          uint256 internal immutable INITIAL_CHAIN_ID;
                      
                          bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
                      
                          mapping(address => uint256) public nonces;
                      
                          /*//////////////////////////////////////////////////////////////
                                                     CONSTRUCTOR
                          //////////////////////////////////////////////////////////////*/
                      
                          constructor(
                              string memory _name,
                              string memory _symbol,
                              uint8 _decimals
                          ) {
                              name = _name;
                              symbol = _symbol;
                              decimals = _decimals;
                      
                              INITIAL_CHAIN_ID = block.chainid;
                              INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
                          }
                      
                          /*//////////////////////////////////////////////////////////////
                                                     ERC20 LOGIC
                          //////////////////////////////////////////////////////////////*/
                      
                          function approve(address spender, uint256 amount) public virtual returns (bool) {
                              allowance[msg.sender][spender] = amount;
                      
                              emit Approval(msg.sender, spender, amount);
                      
                              return true;
                          }
                      
                          function transfer(address to, uint256 amount) public virtual returns (bool) {
                              balanceOf[msg.sender] -= amount;
                      
                              // Cannot overflow because the sum of all user
                              // balances can't exceed the max uint256 value.
                              unchecked {
                                  balanceOf[to] += amount;
                              }
                      
                              emit Transfer(msg.sender, to, amount);
                      
                              return true;
                          }
                      
                          function transferFrom(
                              address from,
                              address to,
                              uint256 amount
                          ) public virtual returns (bool) {
                              uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
                      
                              if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
                      
                              balanceOf[from] -= amount;
                      
                              // Cannot overflow because the sum of all user
                              // balances can't exceed the max uint256 value.
                              unchecked {
                                  balanceOf[to] += amount;
                              }
                      
                              emit Transfer(from, to, amount);
                      
                              return true;
                          }
                      
                          /*//////////////////////////////////////////////////////////////
                                                   EIP-2612 LOGIC
                          //////////////////////////////////////////////////////////////*/
                      
                          function permit(
                              address owner,
                              address spender,
                              uint256 value,
                              uint256 deadline,
                              uint8 v,
                              bytes32 r,
                              bytes32 s
                          ) public virtual {
                              require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
                      
                              // Unchecked because the only math done is incrementing
                              // the owner's nonce which cannot realistically overflow.
                              unchecked {
                                  address recoveredAddress = ecrecover(
                                      keccak256(
                                          abi.encodePacked(
                                              "\x19\x01",
                                              DOMAIN_SEPARATOR(),
                                              keccak256(
                                                  abi.encode(
                                                      keccak256(
                                                          "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                                      ),
                                                      owner,
                                                      spender,
                                                      value,
                                                      nonces[owner]++,
                                                      deadline
                                                  )
                                              )
                                          )
                                      ),
                                      v,
                                      r,
                                      s
                                  );
                      
                                  require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
                      
                                  allowance[recoveredAddress][spender] = value;
                              }
                      
                              emit Approval(owner, spender, value);
                          }
                      
                          function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
                              return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
                          }
                      
                          function computeDomainSeparator() internal view virtual returns (bytes32) {
                              return
                                  keccak256(
                                      abi.encode(
                                          keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                                          keccak256(bytes(name)),
                                          keccak256("1"),
                                          block.chainid,
                                          address(this)
                                      )
                                  );
                          }
                      
                          /*//////////////////////////////////////////////////////////////
                                              INTERNAL MINT/BURN LOGIC
                          //////////////////////////////////////////////////////////////*/
                      
                          function _mint(address to, uint256 amount) internal virtual {
                              totalSupply += amount;
                      
                              // Cannot overflow because the sum of all user
                              // balances can't exceed the max uint256 value.
                              unchecked {
                                  balanceOf[to] += amount;
                              }
                      
                              emit Transfer(address(0), to, amount);
                          }
                      
                          function _burn(address from, uint256 amount) internal virtual {
                              balanceOf[from] -= amount;
                      
                              // Cannot underflow because a user's balance
                              // will never be larger than the total supply.
                              unchecked {
                                  totalSupply -= amount;
                              }
                      
                              emit Transfer(from, address(0), amount);
                          }
                      }
                      
                      /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
                      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
                      /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
                      /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
                      library SafeTransferLib {
                          /*//////////////////////////////////////////////////////////////
                                                   ETH OPERATIONS
                          //////////////////////////////////////////////////////////////*/
                      
                          function safeTransferETH(address to, uint256 amount) internal {
                              bool success;
                      
                              /// @solidity memory-safe-assembly
                              assembly {
                                  // Transfer the ETH and store if it succeeded or not.
                                  success := call(gas(), to, amount, 0, 0, 0, 0)
                              }
                      
                              require(success, "ETH_TRANSFER_FAILED");
                          }
                      
                          /*//////////////////////////////////////////////////////////////
                                                  ERC20 OPERATIONS
                          //////////////////////////////////////////////////////////////*/
                      
                          function safeTransferFrom(
                              ERC20 token,
                              address from,
                              address to,
                              uint256 amount
                          ) internal {
                              bool success;
                      
                              /// @solidity memory-safe-assembly
                              assembly {
                                  // Get a pointer to some free memory.
                                  let freeMemoryPointer := mload(0x40)
                      
                                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                                  mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
                                  mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
                                  mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
                                  mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.
                      
                                  success := and(
                                      // Set success to whether the call reverted, if not we check it either
                                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                                      // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                      // Counterintuitively, this call must be positioned second to the or() call in the
                                      // surrounding and() call or else returndatasize() will be zero during the computation.
                                      call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
                                  )
                              }
                      
                              require(success, "TRANSFER_FROM_FAILED");
                          }
                      
                          function safeTransfer(
                              ERC20 token,
                              address to,
                              uint256 amount
                          ) internal {
                              bool success;
                      
                              /// @solidity memory-safe-assembly
                              assembly {
                                  // Get a pointer to some free memory.
                                  let freeMemoryPointer := mload(0x40)
                      
                                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                                  mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
                                  mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
                                  mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
                      
                                  success := and(
                                      // Set success to whether the call reverted, if not we check it either
                                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                                      // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                      // Counterintuitively, this call must be positioned second to the or() call in the
                                      // surrounding and() call or else returndatasize() will be zero during the computation.
                                      call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                                  )
                              }
                      
                              require(success, "TRANSFER_FAILED");
                          }
                      
                          function safeApprove(
                              ERC20 token,
                              address to,
                              uint256 amount
                          ) internal {
                              bool success;
                      
                              /// @solidity memory-safe-assembly
                              assembly {
                                  // Get a pointer to some free memory.
                                  let freeMemoryPointer := mload(0x40)
                      
                                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                                  mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
                                  mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
                                  mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
                      
                                  success := and(
                                      // Set success to whether the call reverted, if not we check it either
                                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                                      // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                      // Counterintuitively, this call must be positioned second to the or() call in the
                                      // surrounding and() call or else returndatasize() will be zero during the computation.
                                      call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                                  )
                              }
                      
                              require(success, "APPROVE_FAILED");
                          }
                      }
                      
                      /**
                       * @title ISocketGateway
                       * @notice Interface for SocketGateway functions.
                       * @dev functions can be added here for invocation from external contracts or off-chain
                       * @author Socket dot tech.
                       */
                      interface ISocketGateway {
                          /**
                           * @notice Request-struct for controllerRequests
                           * @dev ensure the value for data is generated using the function-selectors defined in the controllerImplementation contracts
                           */
                          struct SocketControllerRequest {
                              // controllerId is the id mapped to the controllerAddress
                              uint32 controllerId;
                              // transactionImplData generated off-chain or by caller using function-selector of the controllerContract
                              bytes data;
                          }
                      
                          // @notice view to get owner-address
                          function owner() external view returns (address);
                      }
                      
                      error CelerRefundNotReady();
                      error OnlySocketDeployer();
                      error OnlySocketGatewayOwner();
                      error OnlySocketGateway();
                      error OnlyOwner();
                      error OnlyNominee();
                      error TransferIdExists();
                      error TransferIdDoesnotExist();
                      error Address0Provided();
                      error SwapFailed();
                      error UnsupportedInterfaceId();
                      error InvalidCelerRefund();
                      error CelerAlreadyRefunded();
                      error IncorrectBridgeRatios();
                      error ZeroAddressNotAllowed();
                      error ArrayLengthMismatch();
                      error PartialSwapsNotAllowed();
                      
                      /**
                       * @title Abstract Implementation Contract.
                       * @notice All Swap Implementation will follow this interface.
                       * @author Socket dot tech.
                       */
                      abstract contract SwapImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                      
                          /// @notice Address used to identify if it is a native token transfer or not
                          address public immutable NATIVE_TOKEN_ADDRESS =
                              address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
                      
                          /// @notice immutable variable to store the socketGateway address
                          address public immutable socketGateway;
                      
                          /// @notice immutable variable to store the socketGateway address
                          address public immutable socketDeployFactory;
                      
                          /// @notice FunctionSelector used to delegatecall to the performAction function of swap-router-implementation
                          bytes4 public immutable SWAP_FUNCTION_SELECTOR =
                              bytes4(
                                  keccak256("performAction(address,address,uint256,address,bytes)")
                              );
                      
                          /// @notice FunctionSelector used to delegatecall to the performActionWithIn function of swap-router-implementation
                          bytes4 public immutable SWAP_WITHIN_FUNCTION_SELECTOR =
                              bytes4(keccak256("performActionWithIn(address,address,uint256,bytes)"));
                      
                          /****************************************
                           *               EVENTS                 *
                           ****************************************/
                      
                          event SocketSwapTokens(
                              address fromToken,
                              address toToken,
                              uint256 buyAmount,
                              uint256 sellAmount,
                              bytes32 routeName,
                              address receiver,
                              bytes32 metadata
                          );
                      
                          /**
                           * @notice Construct the base for all SwapImplementations.
                           * @param _socketGateway Socketgateway address, an immutable variable to set.
                           */
                          constructor(address _socketGateway, address _socketDeployFactory) {
                              socketGateway = _socketGateway;
                              socketDeployFactory = _socketDeployFactory;
                          }
                      
                          /****************************************
                           *               MODIFIERS              *
                           ****************************************/
                      
                          /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used
                          modifier isSocketGatewayOwner() {
                              if (msg.sender != ISocketGateway(socketGateway).owner()) {
                                  revert OnlySocketGatewayOwner();
                              }
                              _;
                          }
                      
                          /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used
                          modifier isSocketDeployFactory() {
                              if (msg.sender != socketDeployFactory) {
                                  revert OnlySocketDeployer();
                              }
                              _;
                          }
                      
                          /****************************************
                           *    RESTRICTED FUNCTIONS              *
                           ****************************************/
                      
                          /**
                           * @notice function to rescue the ERC20 tokens in the Swap-Implementation contract
                           * @notice this is a function restricted to Owner of SocketGateway only
                           * @param token address of ERC20 token being rescued
                           * @param userAddress receipient address to which ERC20 tokens will be rescued to
                           * @param amount amount of ERC20 tokens being rescued
                           */
                          function rescueFunds(
                              address token,
                              address userAddress,
                              uint256 amount
                          ) external isSocketGatewayOwner {
                              ERC20(token).safeTransfer(userAddress, amount);
                          }
                      
                          /**
                           * @notice function to rescue the native-balance in the  Swap-Implementation contract
                           * @notice this is a function restricted to Owner of SocketGateway only
                           * @param userAddress receipient address to which native-balance will be rescued to
                           * @param amount amount of native balance tokens being rescued
                           */
                          function rescueEther(
                              address payable userAddress,
                              uint256 amount
                          ) external isSocketGatewayOwner {
                              userAddress.transfer(amount);
                          }
                      
                          function killme() external isSocketDeployFactory {
                              selfdestruct(payable(msg.sender));
                          }
                      
                          /******************************
                           *    VIRTUAL FUNCTIONS       *
                           *****************************/
                      
                          /**
                           * @notice function to swap tokens on the chain
                           *         All swap implementation contracts must implement this function
                           * @param fromToken token to be swapped
                           * @param  toToken token to which fromToken has to be swapped
                           * @param amount amount of fromToken being swapped
                           * @param receiverAddress recipient address of toToken
                           * @param data encoded value of properties in the swapData Struct
                           */
                          function performAction(
                              address fromToken,
                              address toToken,
                              uint256 amount,
                              address receiverAddress,
                              bytes32 metadata,
                              bytes memory data
                          ) external payable virtual returns (uint256);
                      
                          /**
                           * @notice function to swapWith - swaps tokens on the chain to socketGateway as recipient
                           *         All swap implementation contracts must implement this function
                           * @param fromToken token to be swapped
                           * @param toToken token to which fromToken has to be swapped
                           * @param amount amount of fromToken being swapped
                           * @param swapExtraData encoded value of properties in the swapData Struct
                           */
                          function performActionWithIn(
                              address fromToken,
                              address toToken,
                              uint256 amount,
                              bytes32 metadata,
                              bytes memory swapExtraData
                          ) external payable virtual returns (uint256, address);
                      }
                      
                      bytes32 constant ACROSS = keccak256("Across");
                      
                      bytes32 constant ANYSWAP = keccak256("Anyswap");
                      
                      bytes32 constant CBRIDGE = keccak256("CBridge");
                      
                      bytes32 constant HOP = keccak256("Hop");
                      
                      bytes32 constant HYPHEN = keccak256("Hyphen");
                      
                      bytes32 constant NATIVE_OPTIMISM = keccak256("NativeOptimism");
                      
                      bytes32 constant NATIVE_ARBITRUM = keccak256("NativeArbitrum");
                      
                      bytes32 constant NATIVE_POLYGON = keccak256("NativePolygon");
                      
                      bytes32 constant REFUEL = keccak256("Refuel");
                      
                      bytes32 constant STARGATE = keccak256("Stargate");
                      
                      bytes32 constant ONEINCH = keccak256("OneInch");
                      
                      bytes32 constant ZEROX = keccak256("Zerox");
                      
                      bytes32 constant RAINBOW = keccak256("Rainbow");
                      
                      bytes32 constant CCTP = keccak256("cctp");
                      
                      bytes32 constant CONNEXT = keccak256("Connext");
                      
                      bytes32 constant SYNAPSE = keccak256("Synapse");
                      
                      bytes32 constant ZKSYNC = keccak256("ZkSync");
                      
                      bytes32 constant SYMBIOSIS = keccak256("Symbiosis");
                      
                      /**
                       * @title ZeroX-Swap-Route Implementation
                       * @notice Route implementation with functions to swap tokens via ZeroX-Swap
                       * Called via SocketGateway if the routeId in the request maps to the routeId of ZeroX-Swap-Implementation
                       * @author Socket dot tech.
                       */
                      contract ZeroXSwapImpl is SwapImplBase {
                          /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens
                          using SafeTransferLib for ERC20;
                      
                          bytes32 public immutable ZeroXIdentifier = ZEROX;
                      
                          /// @notice unique name to identify the router, used to emit event upon successful bridging
                          bytes32 public immutable NAME = keccak256("Zerox-Router");
                      
                          /// @notice address of ZeroX-Exchange-Proxy to swap the tokens on Chain
                          address payable public immutable zeroXExchangeProxy;
                      
                          /// @notice socketGatewayAddress to be initialised via storage variable SwapImplBase
                          /// @notice ZeroXExchangeProxy contract is payable to allow ethereum swaps
                          /// @dev ensure _zeroXExchangeProxy are set properly for the chainId in which the contract is being deployed
                          constructor(
                              address _zeroXExchangeProxy,
                              address _socketGateway,
                              address _socketDeployFactory
                          ) SwapImplBase(_socketGateway, _socketDeployFactory) {
                              zeroXExchangeProxy = payable(_zeroXExchangeProxy);
                          }
                      
                          receive() external payable {}
                      
                          fallback() external payable {}
                      
                          /**
                           * @notice function to swap tokens on the chain and transfer to receiver address
                           * @dev This is called only when there is a request for a swap.
                           * @param fromToken token to be swapped
                           * @param toToken token to which fromToken is to be swapped
                           * @param amount amount to be swapped
                           * @param receiverAddress address of toToken recipient
                           * @param swapExtraData data required for zeroX Exchange to get the swap done
                           */
                          function performAction(
                              address fromToken,
                              address toToken,
                              uint256 amount,
                              address receiverAddress,
                              bytes32 metadata,
                              bytes calldata swapExtraData
                          ) external payable override returns (uint256) {
                              uint256 _initialBalanceTokenOut;
                              uint256 _finalBalanceTokenOut;
                      
                              uint256 _initialBalanceTokenIn;
                              uint256 _finalBalanceTokenIn;
                      
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  ERC20(fromToken).safeTransferFrom(
                                      msg.sender,
                                      socketGateway,
                                      amount
                                  );
                                  ERC20(fromToken).safeApprove(zeroXExchangeProxy, amount);
                              }
                      
                              if (toToken != NATIVE_TOKEN_ADDRESS) {
                                  _initialBalanceTokenOut = ERC20(toToken).balanceOf(socketGateway);
                              } else {
                                  _initialBalanceTokenOut = address(this).balance;
                              }
                      
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  _initialBalanceTokenIn = ERC20(fromToken).balanceOf(socketGateway);
                              } else {
                                  _initialBalanceTokenIn = address(this).balance;
                              }
                      
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  // solhint-disable-next-line
                                  (bool success, ) = zeroXExchangeProxy.call(swapExtraData);
                      
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                              } else {
                                  (bool success, ) = zeroXExchangeProxy.call{value: amount}(
                                      swapExtraData
                                  );
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                              }
                      
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  _finalBalanceTokenIn = ERC20(fromToken).balanceOf(socketGateway);
                              } else {
                                  _finalBalanceTokenIn = address(this).balance;
                              }
                              if (_finalBalanceTokenIn > _initialBalanceTokenIn - amount)
                                  revert PartialSwapsNotAllowed();
                      
                              if (toToken != NATIVE_TOKEN_ADDRESS) {
                                  _finalBalanceTokenOut = ERC20(toToken).balanceOf(socketGateway);
                              } else {
                                  _finalBalanceTokenOut = address(this).balance;
                              }
                      
                              uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;
                      
                              if (toToken == NATIVE_TOKEN_ADDRESS) {
                                  payable(receiverAddress).transfer(returnAmount);
                              } else {
                                  ERC20(toToken).transfer(receiverAddress, returnAmount);
                              }
                      
                              emit SocketSwapTokens(
                                  fromToken,
                                  toToken,
                                  returnAmount,
                                  amount,
                                  ZeroXIdentifier,
                                  receiverAddress,
                                  metadata
                              );
                      
                              return returnAmount;
                          }
                      
                          /**
                           * @notice function to swapWithIn SocketGateway - swaps tokens on the chain to socketGateway as recipient
                           * @param fromToken token to be swapped
                           * @param toToken token to which fromToken has to be swapped
                           * @param amount amount of fromToken being swapped
                           * @param swapExtraData encoded value of properties in the swapData Struct
                           * @return swapped amount (in toToken Address)
                           */
                          function performActionWithIn(
                              address fromToken,
                              address toToken,
                              uint256 amount,
                              bytes32 metadata,
                              bytes calldata swapExtraData
                          ) external payable override returns (uint256, address) {
                              uint256 _initialBalanceTokenOut;
                              uint256 _finalBalanceTokenOut;
                      
                              uint256 _initialBalanceTokenIn;
                              uint256 _finalBalanceTokenIn;
                      
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  ERC20(fromToken).safeTransferFrom(
                                      msg.sender,
                                      address(this),
                                      amount
                                  );
                                  ERC20(fromToken).safeApprove(zeroXExchangeProxy, amount);
                              }
                      
                              if (toToken != NATIVE_TOKEN_ADDRESS) {
                                  _initialBalanceTokenOut = ERC20(toToken).balanceOf(socketGateway);
                              } else {
                                  _initialBalanceTokenOut = address(this).balance;
                              }
                      
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  _initialBalanceTokenIn = ERC20(fromToken).balanceOf(socketGateway);
                              } else {
                                  _initialBalanceTokenIn = address(this).balance;
                              }
                      
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  // solhint-disable-next-line
                                  (bool success, ) = zeroXExchangeProxy.call(swapExtraData);
                      
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                              } else {
                                  (bool success, ) = zeroXExchangeProxy.call{value: amount}(
                                      swapExtraData
                                  );
                                  if (!success) {
                                      revert SwapFailed();
                                  }
                              }
                      
                              if (fromToken != NATIVE_TOKEN_ADDRESS) {
                                  _finalBalanceTokenIn = ERC20(fromToken).balanceOf(socketGateway);
                              } else {
                                  _finalBalanceTokenIn = address(this).balance;
                              }
                              if (_finalBalanceTokenIn > _initialBalanceTokenIn - amount)
                                  revert PartialSwapsNotAllowed();
                      
                              if (toToken != NATIVE_TOKEN_ADDRESS) {
                                  _finalBalanceTokenOut = ERC20(toToken).balanceOf(socketGateway);
                              } else {
                                  _finalBalanceTokenOut = address(this).balance;
                              }
                      
                              emit SocketSwapTokens(
                                  fromToken,
                                  toToken,
                                  _finalBalanceTokenOut - _initialBalanceTokenOut,
                                  amount,
                                  ZeroXIdentifier,
                                  socketGateway,
                                  metadata
                              );
                      
                              return (_finalBalanceTokenOut - _initialBalanceTokenOut, toToken);
                          }
                      }