ETH Price: $2,982.31 (+7.64%)
Gas: 6.86 Gwei

Transaction Decoder

Block:
22887743 at Jul-10-2025 08:59:47 AM +UTC
Transaction Fee:
0.00060491690012333 ETH $1.80
Gas Used:
268,654 Gas / 2.251657895 Gwei

Emitted Events:

211 EntryPoint.BeforeExecution( )
212 FiatTokenProxy.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x00000000000000000000000082c2700fabedfd4eb672380aa02cd571f62038c3, 0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae, 000000000000000000000000000000000000000000000000000000001da5b8c0 )
213 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000082c2700fabedfd4eb672380aa02cd571f62038c3, 0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae, 000000000000000000000000000000000000000000000000000000001da5b8c0 )
214 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae, 0x000000000000000000000000f70da97812cb96acdf810712aa562db8dfa3dbef, 000000000000000000000000000000000000000000000000000000001da5b8c0 )
215 LiFiDiamond.0xcba69f43792f9f399347222505213b55af8e0b0b54b893085c2e27ecbe1644f1( 0xcba69f43792f9f399347222505213b55af8e0b0b54b893085c2e27ecbe1644f1, 0000000000000000000000000000000000000000000000000000000000000020, e2c796989ceebee30d10ee712ce56ff851801c66dbab955a8a35b826b8ab4d87, 0000000000000000000000000000000000000000000000000000000000000140, 0000000000000000000000000000000000000000000000000000000000000180, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 00000000000000000000000082c2700fabedfd4eb672380aa02cd571f62038c3, 000000000000000000000000000000000000000000000000000000001da5b8c0, 000000000000000000000000000000000000000000000000000000000000a4b1, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000005, 72656c6179000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000015, 616d626972652d657874656e73696f6e2d70726f640000000000000000000000 )
216 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000082c2700fabedfd4eb672380aa02cd571f62038c3, 0x000000000000000000000000942f9ce5d9a33a82f88d233aeb3292e680230348, 00000000000000000000000000000000000000000000000000000000002799ab )
217 EntryPoint.UserOperationEvent( userOpHash=FDF89E2C463E1C157226491049970A0178B68039AFAE13E071812A65BE5B72F6, sender=0x82c2700fabedfd4eb672380aa02cd571f62038c3, paymaster=AmbirePaymaster, nonce=0, success=True, actualGasCost=682738264027176, actualGasUsed=350723 )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...6f37da032
(Entry Point 0.7.0)
110.618900507878050454 Eth110.618217769614023278 Eth0.000682738264027176
0x1231DEB6...7486F4EaE
(LI.FI: LiFi Diamond)
0x4337002C...1e1D4A93d
(Pimlico: ERC-4337 Bundler 2)
0.188650331751118003 Eth
Nonce: 26753
0.188728153115021849 Eth
Nonce: 26754
0.000077821363903846
(Titan Builder)
49.017804679764114391 Eth49.017902872366700873 Eth0.000098192602586482
0x82C2700f...1F62038C3
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 22892026802761554885710270109960457884771093328183258989
0xA0b86991...E3606eB48

Execution Trace

EntryPoint.handleOps( ops=, beneficiary=0x4337002C5702CE424Cb62A56CA038e31e1D4A93d )
  • 0x82c2700fabedfd4eb672380aa02cd571f62038c3.19822f7c( )
    • Null: 0x000...001.22c175f1( )
    • AmbirePaymaster.validatePaymasterUserOp( userOp=[{name:sender, type:address, order:1, indexed:false, value:0x82C2700fabedFd4Eb672380aA02cD571F62038C3, valueString:0x82C2700fabedFd4Eb672380aA02cD571F62038C3}, {name:nonce, type:uint256, order:2, indexed:false, value:0, valueString:0}, {name:initCode, type:bytes, order:3, indexed:false, value:0x, valueString:0x}, {name:callData, type:bytes, order:4, indexed:false, value:0xABC5345E000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004E0000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095EA7B30000000000000000000000001231DEB6F5749EF6CE6943A275A1D3E7486F4EAE000000000000000000000000000000000000000000000000000000001DA5B8C0000000000000000000000000000000000000000000000000000000000000000000000000000000001231DEB6F5749EF6CE6943A275A1D3E7486F4EAE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000304AE32859000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200E2C796989CEEBEE30D10EE712CE56FF851801C66DBAB955A8A35B826B8AB4D87000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB4800000000000000000000000082C2700FABEDFD4EB672380AA02CD571F62038C3000000000000000000000000000000000000000000000000000000001DA5B8C0000000000000000000000000000000000000000000000000000000000000A4B100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000572656C61790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015616D626972652D657874656E73696F6E2D70726F640000000000000000000000D5581A6AAB9C9363A59F81DA76B3C6CF2E644E9B51D1762039073446ACB6903500000000000000000000000082C2700FABEDFD4EB672380AA02CD571F62038C3000000000000000000000000AF88D065E77C8CC2239327C5EDB3A432268E583100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000041B4FC9A97F6E6F3F5B6F9EE530ADC542629DE44A7AD4F4F16E7D55113AE2B5DC357BC580E440771EDCD12A91EA1B683DF73EA62BB077085BC2C513510B34E5A231C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044A9059CBB000000000000000000000000942F9CE5D9A33A82F88D233AEB3292E68023034800000000000000000000000000000000000000000000000000000000002799AB00000000000000000000000000000000000000000000000000000000, valueString:0xABC5345E000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004E0000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095EA7B30000000000000000000000001231DEB6F5749EF6CE6943A275A1D3E7486F4EAE000000000000000000000000000000000000000000000000000000001DA5B8C0000000000000000000000000000000000000000000000000000000000000000000000000000000001231DEB6F5749EF6CE6943A275A1D3E7486F4EAE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000304AE32859000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200E2C796989CEEBEE30D10EE712CE56FF851801C66DBAB955A8A35B826B8AB4D87000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB4800000000000000000000000082C2700FABEDFD4EB672380AA02CD571F62038C3000000000000000000000000000000000000000000000000000000001DA5B8C0000000000000000000000000000000000000000000000000000000000000A4B100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000572656C61790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015616D626972652D657874656E73696F6E2D70726F640000000000000000000000D5581A6AAB9C9363A59F81DA76B3C6CF2E644E9B51D1762039073446ACB6903500000000000000000000000082C2700FABEDFD4EB672380AA02CD571F62038C3000000000000000000000000AF88D065E77C8CC2239327C5EDB3A432268E583100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000041B4FC9A97F6E6F3F5B6F9EE530ADC542629DE44A7AD4F4F16E7D55113AE2B5DC357BC580E440771EDCD12A91EA1B683DF73EA62BB077085BC2C513510B34E5A231C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044A9059CBB000000000000000000000000942F9CE5D9A33A82F88D233AEB3292E68023034800000000000000000000000000000000000000000000000000000000002799AB00000000000000000000000000000000000000000000000000000000}, {name:accountGasLimits, type:bytes32, order:5, indexed:false, value:00000000000000000000000000010CD900000000000000000000000000036B39, valueString:00000000000000000000000000010CD900000000000000000000000000036B39}, {name:preVerificationGas, type:uint256, order:6, indexed:false, value:100696, valueString:100696}, {name:gasFees, type:bytes32, order:7, indexed:false, value:000000000000000000000000039B2820000000000000000000000000AB27C3D5, valueString:000000000000000000000000039B2820000000000000000000000000AB27C3D5}, {name:paymasterAndData, type:bytes, order:8, indexed:false, value:0xA8B267C68715FA1DCA055993149F30217B572CF000000000000000000000000000006ED80000000000000000000000000000000100000000000000000000000000000000000000000000000000000000686F81B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000427D66D8D8799E0B762080D60B2868B14889A3176D8D6363BDB5633649056E7F7C49980D90468CC032989DC9DD30F958D986301D148A042AFEDABCC58698C7B9F41C00000000000000000000000000000000000000000000000000000000000000, valueString:0xA8B267C68715FA1DCA055993149F30217B572CF000000000000000000000000000006ED80000000000000000000000000000000100000000000000000000000000000000000000000000000000000000686F81B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000427D66D8D8799E0B762080D60B2868B14889A3176D8D6363BDB5633649056E7F7C49980D90468CC032989DC9DD30F958D986301D148A042AFEDABCC58698C7B9F41C00000000000000000000000000000000000000000000000000000000000000}, {name:signature, type:bytes, order:9, indexed:false, value:0xEF86EEF60298E31E6F69C27A400A0519630CDAD924FFB5A2BC6A67C31C37B7FE594ECE2FD761C5A28FD859FDEF0CEA7FFD796B4DD7C2448FE82735A65E5D4E521C00, valueString:0xEF86EEF60298E31E6F69C27A400A0519630CDAD924FFB5A2BC6A67C31C37B7FE594ECE2FD761C5A28FD859FDEF0CEA7FFD796B4DD7C2448FE82735A65E5D4E521C00}], FDF89E2C463E1C157226491049970A0178B68039AFAE13E071812A65BE5B72F6, 1211647990657215 ) => ( context=0x, validationData=2560752789669955550240034848015415932099189478934089564160 )
      • Null: 0x000...001.b34fc22d( )
      • EntryPoint.innerHandleOp( callData=0xABC5345E000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004E0000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095EA7B30000000000000000000000001231DEB6F5749EF6CE6943A275A1D3E7486F4EAE000000000000000000000000000000000000000000000000000000001DA5B8C0000000000000000000000000000000000000000000000000000000000000000000000000000000001231DEB6F5749EF6CE6943A275A1D3E7486F4EAE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000304AE32859000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200E2C796989CEEBEE30D10EE712CE56FF851801C66DBAB955A8A35B826B8AB4D87000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB4800000000000000000000000082C2700FABEDFD4EB672380AA02CD571F62038C3000000000000000000000000000000000000000000000000000000001DA5B8C0000000000000000000000000000000000000000000000000000000000000A4B100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000572656C61790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015616D626972652D657874656E73696F6E2D70726F640000000000000000000000D5581A6AAB9C9363A59F81DA76B3C6CF2E644E9B51D1762039073446ACB6903500000000000000000000000082C2700FABEDFD4EB672380AA02CD571F62038C3000000000000000000000000AF88D065E77C8CC2239327C5EDB3A432268E583100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000041B4FC9A97F6E6F3F5B6F9EE530ADC542629DE44A7AD4F4F16E7D55113AE2B5DC357BC580E440771EDCD12A91EA1B683DF73EA62BB077085BC2C513510B34E5A231C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044A9059CBB000000000000000000000000942F9CE5D9A33A82F88D233AEB3292E68023034800000000000000000000000000000000000000000000000000000000002799AB00000000000000000000000000000000000000000000000000000000, opInfo=[{name:mUserOp, type:tuple, order:1, indexed:false, value:[{name:sender, type:address, order:1, indexed:false, value:0x82C2700fabedFd4Eb672380aA02cD571F62038C3, valueString:0x82C2700fabedFd4Eb672380aA02cD571F62038C3}, {name:nonce, type:uint256, order:2, indexed:false, value:0, valueString:0}, {name:verificationGasLimit, type:uint256, order:3, indexed:false, value:68825, valueString:68825}, {name:callGasLimit, type:uint256, order:4, indexed:false, value:224057, valueString:224057}, {name:paymasterVerificationGasLimit, type:uint256, order:5, indexed:false, value:28376, valueString:28376}, {name:paymasterPostOpGasLimit, type:uint256, order:6, indexed:false, value:1, valueString:1}, {name:preVerificationGas, type:uint256, order:7, indexed:false, value:100696, valueString:100696}, {name:paymaster, type:address, order:8, indexed:false, value:0xA8B267C68715FA1Dca055993149f30217B572Cf0, valueString:0xA8B267C68715FA1Dca055993149f30217B572Cf0}, {name:maxFeePerGas, type:uint256, order:9, indexed:false, value:2871509973, valueString:2871509973}, {name:maxPriorityFeePerGas, type:uint256, order:10, indexed:false, value:60500000, valueString:60500000}], valueString:[{name:sender, type:address, order:1, indexed:false, value:0x82C2700fabedFd4Eb672380aA02cD571F62038C3, valueString:0x82C2700fabedFd4Eb672380aA02cD571F62038C3}, {name:nonce, type:uint256, order:2, indexed:false, value:0, valueString:0}, {name:verificationGasLimit, type:uint256, order:3, indexed:false, value:68825, valueString:68825}, {name:callGasLimit, type:uint256, order:4, indexed:false, value:224057, valueString:224057}, {name:paymasterVerificationGasLimit, type:uint256, order:5, indexed:false, value:28376, valueString:28376}, {name:paymasterPostOpGasLimit, type:uint256, order:6, indexed:false, value:1, valueString:1}, {name:preVerificationGas, type:uint256, order:7, indexed:false, value:100696, valueString:100696}, {name:paymaster, type:address, order:8, indexed:false, value:0xA8B267C68715FA1Dca055993149f30217B572Cf0, valueString:0xA8B267C68715FA1Dca055993149f30217B572Cf0}, {name:maxFeePerGas, type:uint256, order:9, indexed:false, value:2871509973, valueString:2871509973}, {name:maxPriorityFeePerGas, type:uint256, order:10, indexed:false, value:60500000, valueString:60500000}]}, {name:userOpHash, type:bytes32, order:2, indexed:false, value:FDF89E2C463E1C157226491049970A0178B68039AFAE13E071812A65BE5B72F6, valueString:FDF89E2C463E1C157226491049970A0178B68039AFAE13E071812A65BE5B72F6}, {name:prefund, type:uint256, order:3, indexed:false, value:1211647990657215, valueString:1211647990657215}, {name:contextOffset, type:uint256, order:4, indexed:false, value:1216, valueString:1216}, {name:preOpGas, type:uint256, order:5, indexed:false, value:170024, valueString:170024}], context=0x ) => ( actualGasCost=682738264027176 )
        • 0x82c2700fabedfd4eb672380aa02cd571f62038c3.abc5345e( )
          • FiatTokenProxy.095ea7b3( )
            • FiatTokenV2_2.approve( spender=0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE, value=497400000 ) => ( True )
            • LiFiDiamond.ae328590( )
              • RelayFacet.startBridgeTokensViaRelay( _bridgeData=[{name:transactionId, type:bytes32, order:1, indexed:false, value:E2C796989CEEBEE30D10EE712CE56FF851801C66DBAB955A8A35B826B8AB4D87, valueString:E2C796989CEEBEE30D10EE712CE56FF851801C66DBAB955A8A35B826B8AB4D87}, {name:bridge, type:string, order:2, indexed:false, value:relay, valueString:relay}, {name:integrator, type:string, order:3, indexed:false, value:ambire-extension-prod, valueString:ambire-extension-prod}, {name:referrer, type:address, order:4, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:sendingAssetId, type:address, order:5, indexed:false, value:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, valueString:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}, {name:receiver, type:address, order:6, indexed:false, value:0x82C2700fabedFd4Eb672380aA02cD571F62038C3, valueString:0x82C2700fabedFd4Eb672380aA02cD571F62038C3}, {name:minAmount, type:uint256, order:7, indexed:false, value:497400000, valueString:497400000}, {name:destinationChainId, type:uint256, order:8, indexed:false, value:42161, valueString:42161}, {name:hasSourceSwaps, type:bool, order:9, indexed:false, value:false, valueString:False}, {name:hasDestinationCall, type:bool, order:10, indexed:false, value:false, valueString:False}], _relayData=[{name:requestId, type:bytes32, order:1, indexed:false, value:D5581A6AAB9C9363A59F81DA76B3C6CF2E644E9B51D1762039073446ACB69035, valueString:D5581A6AAB9C9363A59F81DA76B3C6CF2E644E9B51D1762039073446ACB69035}, {name:nonEVMReceiver, type:bytes32, order:2, indexed:false, value:00000000000000000000000082C2700FABEDFD4EB672380AA02CD571F62038C3, valueString:00000000000000000000000082C2700FABEDFD4EB672380AA02CD571F62038C3}, {name:receivingAssetId, type:bytes32, order:3, indexed:false, value:000000000000000000000000AF88D065E77C8CC2239327C5EDB3A432268E5831, valueString:000000000000000000000000AF88D065E77C8CC2239327C5EDB3A432268E5831}, {name:signature, type:bytes, order:4, indexed:false, value:0xB4FC9A97F6E6F3F5B6F9EE530ADC542629DE44A7AD4F4F16E7D55113AE2B5DC357BC580E440771EDCD12A91EA1B683DF73EA62BB077085BC2C513510B34E5A231C, valueString:0xB4FC9A97F6E6F3F5B6F9EE530ADC542629DE44A7AD4F4F16E7D55113AE2B5DC357BC580E440771EDCD12A91EA1B683DF73EA62BB077085BC2C513510B34E5A231C}] )
                • Null: 0x000...001.b88ac952( )
                • FiatTokenProxy.70a08231( )
                  • FiatTokenV2_2.balanceOf( account=0x82C2700fabedFd4Eb672380aA02cD571F62038C3 ) => ( 500000000 )
                  • FiatTokenProxy.70a08231( )
                    • FiatTokenV2_2.balanceOf( account=0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE ) => ( 0 )
                    • FiatTokenProxy.23b872dd( )
                      • FiatTokenV2_2.transferFrom( from=0x82C2700fabedFd4Eb672380aA02cD571F62038C3, to=0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE, value=497400000 ) => ( True )
                      • FiatTokenProxy.70a08231( )
                        • FiatTokenV2_2.balanceOf( account=0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE ) => ( 497400000 )
                        • FiatTokenProxy.a9059cbb( )
                          • FiatTokenV2_2.transfer( to=0xf70da97812CB96acDF810712Aa562db8dfA3dbEF, value=497400000 ) => ( True )
                          • FiatTokenProxy.a9059cbb( )
                            • FiatTokenV2_2.transfer( to=0x942f9CE5D9a33a82F88D233AEb3292E680230348, value=2595243 ) => ( True )
                            • ETH 0.000682738264027176 Pimlico: ERC-4337 Bundler 2.CALL( )
                              File 1 of 6: EntryPoint
                              // SPDX-License-Identifier: MIT
                              // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
                              pragma solidity ^0.8.20;
                              import {IERC165} from "./IERC165.sol";
                              /**
                               * @dev Implementation of the {IERC165} interface.
                               *
                               * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
                               * for the additional interface id that will be supported. For example:
                               *
                               * ```solidity
                               * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                               *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
                               * }
                               * ```
                               */
                              abstract contract ERC165 is IERC165 {
                                  /**
                                   * @dev See {IERC165-supportsInterface}.
                                   */
                                  function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                                      return interfaceId == type(IERC165).interfaceId;
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
                              pragma solidity ^0.8.20;
                              /**
                               * @dev Interface of the ERC165 standard, as defined in the
                               * https://eips.ethereum.org/EIPS/eip-165[EIP].
                               *
                               * Implementers can declare support of contract interfaces, which can then be
                               * queried by others ({ERC165Checker}).
                               *
                               * For an implementation, see {ERC165}.
                               */
                              interface IERC165 {
                                  /**
                                   * @dev Returns true if this contract implements the interface defined by
                                   * `interfaceId`. See the corresponding
                                   * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                                   * to learn more about how these ids are created.
                                   *
                                   * This function call must use less than 30 000 gas.
                                   */
                                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
                              }
                              // SPDX-License-Identifier: MIT
                              // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
                              pragma solidity ^0.8.20;
                              /**
                               * @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;
                                  /**
                                   * @dev Unauthorized reentrant call.
                                   */
                                  error ReentrancyGuardReentrantCall();
                                  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
                                      if (_status == ENTERED) {
                                          revert ReentrancyGuardReentrantCall();
                                      }
                                      // 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;
                                  }
                                  /**
                                   * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
                                   * `nonReentrant` function in the call stack.
                                   */
                                  function _reentrancyGuardEntered() internal view returns (bool) {
                                      return _status == ENTERED;
                                  }
                              }
                              // SPDX-License-Identifier: GPL-3.0
                              pragma solidity ^0.8.23;
                              /* solhint-disable avoid-low-level-calls */
                              /* solhint-disable no-inline-assembly */
                              import "../interfaces/IAccount.sol";
                              import "../interfaces/IAccountExecute.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 "./UserOperationLib.sol";
                              import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
                              import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
                              /*
                               * Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
                               * Only one instance required on each chain.
                               */
                              /// @custom:security-contact https://bounty.ethereum.org
                              contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard, ERC165 {
                                  using UserOperationLib for PackedUserOperation;
                                  SenderCreator private immutable _senderCreator = new SenderCreator();
                                  function senderCreator() internal view virtual returns (SenderCreator) {
                                      return _senderCreator;
                                  }
                                  //compensate for innerHandleOps' emit message and deposit refund.
                                  // allow some slack for future gas price changes.
                                  uint256 private constant INNER_GAS_OVERHEAD = 10000;
                                  // Marker for inner call revert on out of gas
                                  bytes32 private constant INNER_OUT_OF_GAS = hex"deaddead";
                                  bytes32 private constant INNER_REVERT_LOW_PREFUND = hex"deadaa51";
                                  uint256 private constant REVERT_REASON_MAX_LEN = 2048;
                                  uint256 private constant PENALTY_PERCENT = 10;
                                  /// @inheritdoc IERC165
                                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                                      // note: solidity "type(IEntryPoint).interfaceId" is without inherited methods but we want to check everything
                                      return interfaceId == (type(IEntryPoint).interfaceId ^ type(IStakeManager).interfaceId ^ type(INonceManager).interfaceId) ||
                                          interfaceId == type(IEntryPoint).interfaceId ||
                                          interfaceId == type(IStakeManager).interfaceId ||
                                          interfaceId == type(INonceManager).interfaceId ||
                                          super.supportsInterface(interfaceId);
                                  }
                                  /**
                                   * 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 operation.
                                   * @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,
                                      PackedUserOperation calldata userOp,
                                      UserOpInfo memory opInfo
                                  )
                                  internal
                                  returns
                                  (uint256 collected) {
                                      uint256 preGas = gasleft();
                                      bytes memory context = getMemoryBytesFromOffset(opInfo.contextOffset);
                                      bool success;
                                      {
                                          uint256 saveFreePtr;
                                          assembly ("memory-safe") {
                                              saveFreePtr := mload(0x40)
                                          }
                                          bytes calldata callData = userOp.callData;
                                          bytes memory innerCall;
                                          bytes4 methodSig;
                                          assembly {
                                              let len := callData.length
                                              if gt(len, 3) {
                                                  methodSig := calldataload(callData.offset)
                                              }
                                          }
                                          if (methodSig == IAccountExecute.executeUserOp.selector) {
                                              bytes memory executeUserOp = abi.encodeCall(IAccountExecute.executeUserOp, (userOp, opInfo.userOpHash));
                                              innerCall = abi.encodeCall(this.innerHandleOp, (executeUserOp, opInfo, context));
                                          } else
                                          {
                                              innerCall = abi.encodeCall(this.innerHandleOp, (callData, opInfo, context));
                                          }
                                          assembly ("memory-safe") {
                                              success := call(gas(), address(), 0, add(innerCall, 0x20), mload(innerCall), 0, 32)
                                              collected := mload(0)
                                              mstore(0x40, saveFreePtr)
                                          }
                                      }
                                      if (!success) {
                                          bytes32 innerRevertCode;
                                          assembly ("memory-safe") {
                                              let len := returndatasize()
                                              if eq(32,len) {
                                                  returndatacopy(0, 0, 32)
                                                  innerRevertCode := mload(0)
                                              }
                                          }
                                          if (innerRevertCode == INNER_OUT_OF_GAS) {
                                              // handleOps was called with gas limit too low. abort entire bundle.
                                              //can only be caused by bundler (leaving not enough gas for inner call)
                                              revert FailedOp(opIndex, "AA95 out of gas");
                                          } else if (innerRevertCode == INNER_REVERT_LOW_PREFUND) {
                                              // innerCall reverted on prefund too low. treat entire prefund as "gas cost"
                                              uint256 actualGas = preGas - gasleft() + opInfo.preOpGas;
                                              uint256 actualGasCost = opInfo.prefund;
                                              emitPrefundTooLow(opInfo);
                                              emitUserOperationEvent(opInfo, false, actualGasCost, actualGas);
                                              collected = actualGasCost;
                                          } else {
                                              emit PostOpRevertReason(
                                                  opInfo.userOpHash,
                                                  opInfo.mUserOp.sender,
                                                  opInfo.mUserOp.nonce,
                                                  Exec.getReturnData(REVERT_REASON_MAX_LEN)
                                              );
                                              uint256 actualGas = preGas - gasleft() + opInfo.preOpGas;
                                              collected = _postExecution(
                                                  IPaymaster.PostOpMode.postOpReverted,
                                                  opInfo,
                                                  context,
                                                  actualGas
                                              );
                                          }
                                      }
                                  }
                                  function emitUserOperationEvent(UserOpInfo memory opInfo, bool success, uint256 actualGasCost, uint256 actualGas) internal virtual {
                                      emit UserOperationEvent(
                                          opInfo.userOpHash,
                                          opInfo.mUserOp.sender,
                                          opInfo.mUserOp.paymaster,
                                          opInfo.mUserOp.nonce,
                                          success,
                                          actualGasCost,
                                          actualGas
                                      );
                                  }
                                  function emitPrefundTooLow(UserOpInfo memory opInfo) internal virtual {
                                      emit UserOperationPrefundTooLow(
                                          opInfo.userOpHash,
                                          opInfo.mUserOp.sender,
                                          opInfo.mUserOp.nonce
                                      );
                                  }
                                  /// @inheritdoc IEntryPoint
                                  function handleOps(
                                      PackedUserOperation[] 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);
                                      }
                                  }
                                  /// @inheritdoc IEntryPoint
                                  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];
                                          PackedUserOperation[] 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);
                                      uint256 opIndex = 0;
                                      for (uint256 a = 0; a < opasLen; a++) {
                                          UserOpsPerAggregator calldata opa = opsPerAggregator[a];
                                          PackedUserOperation[] 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++;
                                          }
                                      }
                                      emit BeforeExecution();
                                      uint256 collected = 0;
                                      opIndex = 0;
                                      for (uint256 a = 0; a < opasLen; a++) {
                                          UserOpsPerAggregator calldata opa = opsPerAggregator[a];
                                          emit SignatureAggregatorChanged(address(opa.aggregator));
                                          PackedUserOperation[] 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);
                                  }
                                  /**
                                   * A memory copy of UserOp static fields only.
                                   * Excluding: callData, initCode and signature. Replacing paymasterAndData with paymaster.
                                   */
                                  struct MemoryUserOp {
                                      address sender;
                                      uint256 nonce;
                                      uint256 verificationGasLimit;
                                      uint256 callGasLimit;
                                      uint256 paymasterVerificationGasLimit;
                                      uint256 paymasterPostOpGasLimit;
                                      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.
                                   * @param callData - The callData to execute.
                                   * @param opInfo   - The UserOpInfo struct.
                                   * @param context  - The context bytes.
                                   * @return actualGasCost - the actual cost in eth this UserOperation paid for gas
                                   */
                                  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;
                                      uint256 callGasLimit = mUserOp.callGasLimit;
                                      unchecked {
                                          // handleOps was called with gas limit too low. abort entire bundle.
                                          if (
                                              gasleft() * 63 / 64 <
                                              callGasLimit +
                                              mUserOp.paymasterPostOpGasLimit +
                                              INNER_GAS_OVERHEAD
                                          ) {
                                              assembly ("memory-safe") {
                                                  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;
                                          return _postExecution(mode, opInfo, context, actualGas);
                                      }
                                  }
                                  /// @inheritdoc IEntryPoint
                                  function getUserOpHash(
                                      PackedUserOperation 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.
                                   * @param userOp  - The user operation.
                                   * @param mUserOp - The memory user operation.
                                   */
                                  function _copyUserOpToMemory(
                                      PackedUserOperation calldata userOp,
                                      MemoryUserOp memory mUserOp
                                  ) internal pure {
                                      mUserOp.sender = userOp.sender;
                                      mUserOp.nonce = userOp.nonce;
                                      (mUserOp.verificationGasLimit, mUserOp.callGasLimit) = UserOperationLib.unpackUints(userOp.accountGasLimits);
                                      mUserOp.preVerificationGas = userOp.preVerificationGas;
                                      (mUserOp.maxPriorityFeePerGas, mUserOp.maxFeePerGas) = UserOperationLib.unpackUints(userOp.gasFees);
                                      bytes calldata paymasterAndData = userOp.paymasterAndData;
                                      if (paymasterAndData.length > 0) {
                                          require(
                                              paymasterAndData.length >= UserOperationLib.PAYMASTER_DATA_OFFSET,
                                              "AA93 invalid paymasterAndData"
                                          );
                                          (mUserOp.paymaster, mUserOp.paymasterVerificationGasLimit, mUserOp.paymasterPostOpGasLimit) = UserOperationLib.unpackPaymasterStaticFields(paymasterAndData);
                                      } else {
                                          mUserOp.paymaster = address(0);
                                          mUserOp.paymasterVerificationGasLimit = 0;
                                          mUserOp.paymasterPostOpGasLimit = 0;
                                      }
                                  }
                                  /**
                                   * Get the required prefunded gas fee amount for an operation.
                                   * @param mUserOp - The user operation in memory.
                                   */
                                  function _getRequiredPrefund(
                                      MemoryUserOp memory mUserOp
                                  ) internal pure returns (uint256 requiredPrefund) {
                                      unchecked {
                                          uint256 requiredGas = mUserOp.verificationGasLimit +
                                              mUserOp.callGasLimit +
                                              mUserOp.paymasterVerificationGasLimit +
                                              mUserOp.paymasterPostOpGasLimit +
                                              mUserOp.preVerificationGas;
                                          requiredPrefund = requiredGas * mUserOp.maxFeePerGas;
                                      }
                                  }
                                  /**
                                   * Create sender smart contract account if init code is provided.
                                   * @param opIndex  - The operation index.
                                   * @param opInfo   - The operation info.
                                   * @param initCode - The init code for the smart contract account.
                                   */
                                  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
                                          );
                                      }
                                  }
                                  /// @inheritdoc IEntryPoint
                                  function getSenderAddress(bytes calldata initCode) public {
                                      address sender = senderCreator().createSender(initCode);
                                      revert SenderAddressResult(sender);
                                  }
                                  /**
                                   * Call account.validateUserOp.
                                   * Revert (with FailedOp) in case validateUserOp reverts, or account didn't send required prefund.
                                   * Decrement account's deposit if needed.
                                   * @param opIndex         - The operation index.
                                   * @param op              - The user operation.
                                   * @param opInfo          - The operation info.
                                   * @param requiredPrefund - The required prefund amount.
                                   */
                                  function _validateAccountPrepayment(
                                      uint256 opIndex,
                                      PackedUserOperation calldata op,
                                      UserOpInfo memory opInfo,
                                      uint256 requiredPrefund,
                                      uint256 verificationGasLimit
                                  )
                                      internal
                                      returns (
                                          uint256 validationData
                                      )
                                  {
                                      unchecked {
                                          MemoryUserOp memory mUserOp = opInfo.mUserOp;
                                          address sender = mUserOp.sender;
                                          _createSenderIfNeeded(opIndex, opInfo, op.initCode);
                                          address paymaster = mUserOp.paymaster;
                                          uint256 missingAccountFunds = 0;
                                          if (paymaster == address(0)) {
                                              uint256 bal = balanceOf(sender);
                                              missingAccountFunds = bal > requiredPrefund
                                                  ? 0
                                                  : requiredPrefund - bal;
                                          }
                                          try
                                              IAccount(sender).validateUserOp{
                                                  gas: verificationGasLimit
                                              }(op, opInfo.userOpHash, missingAccountFunds)
                                          returns (uint256 _validationData) {
                                              validationData = _validationData;
                                          } catch {
                                              revert FailedOpWithRevert(opIndex, "AA23 reverted", Exec.getReturnData(REVERT_REASON_MAX_LEN));
                                          }
                                          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 = deposit - requiredPrefund;
                                          }
                                      }
                                  }
                                  /**
                                   * 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.
                                   * @param opIndex                            - The operation index.
                                   * @param op                                 - The user operation.
                                   * @param opInfo                             - The operation info.
                                   * @param requiredPreFund                    - The required prefund amount.
                                   */
                                  function _validatePaymasterPrepayment(
                                      uint256 opIndex,
                                      PackedUserOperation calldata op,
                                      UserOpInfo memory opInfo,
                                      uint256 requiredPreFund
                                  ) internal returns (bytes memory context, uint256 validationData) {
                                      unchecked {
                                          uint256 preGas = gasleft();
                                          MemoryUserOp memory mUserOp = opInfo.mUserOp;
                                          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 = deposit - requiredPreFund;
                                          uint256 pmVerificationGasLimit = mUserOp.paymasterVerificationGasLimit;
                                          try
                                              IPaymaster(paymaster).validatePaymasterUserOp{gas: pmVerificationGasLimit}(
                                                  op,
                                                  opInfo.userOpHash,
                                                  requiredPreFund
                                              )
                                          returns (bytes memory _context, uint256 _validationData) {
                                              context = _context;
                                              validationData = _validationData;
                                          } catch {
                                              revert FailedOpWithRevert(opIndex, "AA33 reverted", Exec.getReturnData(REVERT_REASON_MAX_LEN));
                                          }
                                          if (preGas - gasleft() > pmVerificationGasLimit) {
                                              revert FailedOp(opIndex, "AA36 over paymasterVerificationGasLimit");
                                          }
                                      }
                                  }
                                  /**
                                   * Revert if either account validationData or paymaster validationData is expired.
                                   * @param opIndex                 - The operation index.
                                   * @param validationData          - The account validationData.
                                   * @param paymasterValidationData - The paymaster validationData.
                                   * @param expectedAggregator      - The expected aggregator.
                                   */
                                  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");
                                      }
                                  }
                                  /**
                                   * Parse validationData into its components.
                                   * @param validationData - The packed validation data (sigFailed, validAfter, validUntil).
                                   * @return aggregator the aggregator of the validationData
                                   * @return outOfTimeRange true if current time is outside the time range of this validationData.
                                   */
                                  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) and
                                   * 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,
                                      PackedUserOperation calldata userOp,
                                      UserOpInfo memory outOpInfo
                                  )
                                      internal
                                      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 verificationGasLimit = mUserOp.verificationGasLimit;
                                      uint256 maxGasValues = mUserOp.preVerificationGas |
                                          verificationGasLimit |
                                          mUserOp.callGasLimit |
                                          mUserOp.paymasterVerificationGasLimit |
                                          mUserOp.paymasterPostOpGasLimit |
                                          mUserOp.maxFeePerGas |
                                          mUserOp.maxPriorityFeePerGas;
                                      require(maxGasValues <= type(uint120).max, "AA94 gas values overflow");
                                      uint256 requiredPreFund = _getRequiredPrefund(mUserOp);
                                      validationData = _validateAccountPrepayment(
                                          opIndex,
                                          userOp,
                                          outOpInfo,
                                          requiredPreFund,
                                          verificationGasLimit
                                      );
                                      if (!_validateAndUpdateNonce(mUserOp.sender, mUserOp.nonce)) {
                                          revert FailedOp(opIndex, "AA25 invalid account nonce");
                                      }
                                      unchecked {
                                          if (preGas - gasleft() > verificationGasLimit) {
                                              revert FailedOp(opIndex, "AA26 over verificationGasLimit");
                                          }
                                      }
                                      bytes memory context;
                                      if (mUserOp.paymaster != address(0)) {
                                          (context, paymasterValidationData) = _validatePaymasterPrepayment(
                                              opIndex,
                                              userOp,
                                              outOpInfo,
                                              requiredPreFund
                                          );
                                      }
                                      unchecked {
                                          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 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 _postExecution(
                                      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) {
                                                      try IPaymaster(paymaster).postOp{
                                                          gas: mUserOp.paymasterPostOpGasLimit
                                                      }(mode, context, actualGasCost, gasPrice)
                                                      // solhint-disable-next-line no-empty-blocks
                                                      {} catch {
                                                          bytes memory reason = Exec.getReturnData(REVERT_REASON_MAX_LEN);
                                                          revert PostOpReverted(reason);
                                                      }
                                                  }
                                              }
                                          }
                                          actualGas += preGas - gasleft();
                                          // Calculating a penalty for unused execution gas
                                          {
                                              uint256 executionGasLimit = mUserOp.callGasLimit + mUserOp.paymasterPostOpGasLimit;
                                              uint256 executionGasUsed = actualGas - opInfo.preOpGas;
                                              // this check is required for the gas used within EntryPoint and not covered by explicit gas limits
                                              if (executionGasLimit > executionGasUsed) {
                                                  uint256 unusedGas = executionGasLimit - executionGasUsed;
                                                  uint256 unusedGasPenalty = (unusedGas * PENALTY_PERCENT) / 100;
                                                  actualGas += unusedGasPenalty;
                                              }
                                          }
                                          actualGasCost = actualGas * gasPrice;
                                          uint256 prefund = opInfo.prefund;
                                          if (prefund < actualGasCost) {
                                              if (mode == IPaymaster.PostOpMode.postOpReverted) {
                                                  actualGasCost = prefund;
                                                  emitPrefundTooLow(opInfo);
                                                  emitUserOperationEvent(opInfo, false, actualGasCost, actualGas);
                                              } else {
                                                  assembly ("memory-safe") {
                                                      mstore(0, INNER_REVERT_LOW_PREFUND)
                                                      revert(0, 32)
                                                  }
                                              }
                                          } else {
                                              uint256 refund = prefund - actualGasCost;
                                              _incrementDeposit(refundAddress, refund);
                                              bool success = mode == IPaymaster.PostOpMode.opSucceeded;
                                              emitUserOperationEvent(opInfo, 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.
                                   * @param mUserOp - The userOp to get the gas price from.
                                   */
                                  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);
                                      }
                                  }
                                  /**
                                   * The offset of the given bytes in memory.
                                   * @param data - The bytes to get the offset of.
                                   */
                                  function getOffsetOfMemoryBytes(
                                      bytes memory data
                                  ) internal pure returns (uint256 offset) {
                                      assembly {
                                          offset := data
                                      }
                                  }
                                  /**
                                   * The bytes in memory at the given offset.
                                   * @param offset - The offset to get the bytes from.
                                   */
                                  function getMemoryBytesFromOffset(
                                      uint256 offset
                                  ) internal pure returns (bytes memory data) {
                                      assembly ("memory-safe") {
                                          data := offset
                                      }
                                  }
                                  /// @inheritdoc IEntryPoint
                                  function delegateAndRevert(address target, bytes calldata data) external {
                                      (bool success, bytes memory ret) = target.delegatecall(data);
                                      revert DelegateAndRevert(success, ret);
                                  }
                              }
                              // SPDX-License-Identifier: GPL-3.0
                              pragma solidity ^0.8.23;
                              /* solhint-disable no-inline-assembly */
                               /*
                                * For simulation purposes, validateUserOp (and validatePaymasterUserOp)
                                * must return this value in case of signature failure, instead of revert.
                                */
                              uint256 constant SIG_VALIDATION_FAILED = 1;
                              /*
                               * For simulation purposes, validateUserOp (and validatePaymasterUserOp)
                               * return this value on success.
                               */
                              uint256 constant SIG_VALIDATION_SUCCESS = 0;
                              /**
                               * Returned data from validateUserOp.
                               * validateUserOp returns a uint256, which 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.
                               * @param validationData - The packed validation data.
                               */
                              function _parseValidationData(
                                  uint256 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);
                              }
                              /**
                               * 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 ("memory-safe") {
                                          let mem := mload(0x40)
                                          let len := data.length
                                          calldatacopy(mem, data.offset, len)
                                          ret := keccak256(mem, len)
                                      }
                                  }
                              /**
                               * The minimum of two numbers.
                               * @param a - First number.
                               * @param b - Second number.
                               */
                                  function min(uint256 a, uint256 b) pure returns (uint256) {
                                      return a < b ? a : b;
                                  }
                              // SPDX-License-Identifier: GPL-3.0
                              pragma solidity ^0.8.23;
                              import "../interfaces/INonceManager.sol";
                              /**
                               * nonce management functionality
                               */
                              abstract contract NonceManager is INonceManager {
                                  /**
                                   * The next valid sequence number for a given nonce key.
                                   */
                                  mapping(address => mapping(uint192 => uint256)) public nonceSequenceNumber;
                                  /// @inheritdoc INonceManager
                                  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()
                                   * @return true if the nonce was incremented successfully.
                                   *         false if the current nonce doesn't match the given one.
                                   */
                                  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.23;
                              /**
                               * 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 ("memory-safe") {
                                          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.23;
                              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.
                                   * @param addr - The account to query.
                                   */
                                  function _getStakeInfo(
                                      address addr
                                  ) internal view returns (StakeInfo memory info) {
                                      DepositInfo storage depositInfo = deposits[addr];
                                      info.stake = depositInfo.stake;
                                      info.unstakeDelaySec = depositInfo.unstakeDelaySec;
                                  }
                                  /// @inheritdoc IStakeManager
                                  function balanceOf(address account) public view returns (uint256) {
                                      return deposits[account].deposit;
                                  }
                                  receive() external payable {
                                      depositTo(msg.sender);
                                  }
                                  /**
                                   * Increments an account's deposit.
                                   * @param account - The account to increment.
                                   * @param amount  - The amount to increment by.
                                   * @return the updated deposit of this account
                                   */
                                  function _incrementDeposit(address account, uint256 amount) internal returns (uint256) {
                                      DepositInfo storage info = deposits[account];
                                      uint256 newAmount = info.deposit + amount;
                                      info.deposit = newAmount;
                                      return newAmount;
                                  }
                                  /**
                                   * Add to the deposit of the given account.
                                   * @param account - The account to add to.
                                   */
                                  function depositTo(address account) public virtual payable {
                                      uint256 newDeposit = _incrementDeposit(account, msg.value);
                                      emit Deposited(account, newDeposit);
                                  }
                                  /**
                                   * 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 = 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.23;
                              /* solhint-disable no-inline-assembly */
                              import "../interfaces/PackedUserOperation.sol";
                              import {calldataKeccak, min} from "./Helpers.sol";
                              /**
                               * Utility functions helpful when working with UserOperation structs.
                               */
                              library UserOperationLib {
                                  uint256 public constant PAYMASTER_VALIDATION_GAS_OFFSET = 20;
                                  uint256 public constant PAYMASTER_POSTOP_GAS_OFFSET = 36;
                                  uint256 public constant PAYMASTER_DATA_OFFSET = 52;
                                  /**
                                   * Get sender from user operation data.
                                   * @param userOp - The user operation data.
                                   */
                                  function getSender(
                                      PackedUserOperation 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.
                                   * @param userOp - The user operation data.
                                   */
                                  function gasPrice(
                                      PackedUserOperation calldata userOp
                                  ) internal view returns (uint256) {
                                      unchecked {
                                          (uint256 maxPriorityFeePerGas, uint256 maxFeePerGas) = unpackUints(userOp.gasFees);
                                          if (maxFeePerGas == maxPriorityFeePerGas) {
                                              //legacy mode (for networks that don't support basefee opcode)
                                              return maxFeePerGas;
                                          }
                                          return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
                                      }
                                  }
                                  /**
                                   * Pack the user operation data into bytes for hashing.
                                   * @param userOp - The user operation data.
                                   */
                                  function encode(
                                      PackedUserOperation 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);
                                      bytes32 accountGasLimits = userOp.accountGasLimits;
                                      uint256 preVerificationGas = userOp.preVerificationGas;
                                      bytes32 gasFees = userOp.gasFees;
                                      bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);
                                      return abi.encode(
                                          sender, nonce,
                                          hashInitCode, hashCallData,
                                          accountGasLimits, preVerificationGas, gasFees,
                                          hashPaymasterAndData
                                      );
                                  }
                                  function unpackUints(
                                      bytes32 packed
                                  ) internal pure returns (uint256 high128, uint256 low128) {
                                      return (uint128(bytes16(packed)), uint128(uint256(packed)));
                                  }
                                  //unpack just the high 128-bits from a packed value
                                  function unpackHigh128(bytes32 packed) internal pure returns (uint256) {
                                      return uint256(packed) >> 128;
                                  }
                                  // unpack just the low 128-bits from a packed value
                                  function unpackLow128(bytes32 packed) internal pure returns (uint256) {
                                      return uint128(uint256(packed));
                                  }
                                  function unpackMaxPriorityFeePerGas(PackedUserOperation calldata userOp)
                                  internal pure returns (uint256) {
                                      return unpackHigh128(userOp.gasFees);
                                  }
                                  function unpackMaxFeePerGas(PackedUserOperation calldata userOp)
                                  internal pure returns (uint256) {
                                      return unpackLow128(userOp.gasFees);
                                  }
                                  function unpackVerificationGasLimit(PackedUserOperation calldata userOp)
                                  internal pure returns (uint256) {
                                      return unpackHigh128(userOp.accountGasLimits);
                                  }
                                  function unpackCallGasLimit(PackedUserOperation calldata userOp)
                                  internal pure returns (uint256) {
                                      return unpackLow128(userOp.accountGasLimits);
                                  }
                                  function unpackPaymasterVerificationGasLimit(PackedUserOperation calldata userOp)
                                  internal pure returns (uint256) {
                                      return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET : PAYMASTER_POSTOP_GAS_OFFSET]));
                                  }
                                  function unpackPostOpGasLimit(PackedUserOperation calldata userOp)
                                  internal pure returns (uint256) {
                                      return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET : PAYMASTER_DATA_OFFSET]));
                                  }
                                  function unpackPaymasterStaticFields(
                                      bytes calldata paymasterAndData
                                  ) internal pure returns (address paymaster, uint256 validationGasLimit, uint256 postOpGasLimit) {
                                      return (
                                          address(bytes20(paymasterAndData[: PAYMASTER_VALIDATION_GAS_OFFSET])),
                                          uint128(bytes16(paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET : PAYMASTER_POSTOP_GAS_OFFSET])),
                                          uint128(bytes16(paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET : PAYMASTER_DATA_OFFSET]))
                                      );
                                  }
                                  /**
                                   * Hash the user operation data.
                                   * @param userOp - The user operation data.
                                   */
                                  function hash(
                                      PackedUserOperation calldata userOp
                                  ) internal pure returns (bytes32) {
                                      return keccak256(encode(userOp));
                                  }
                              }
                              // SPDX-License-Identifier: GPL-3.0
                              pragma solidity >=0.7.5;
                              import "./PackedUserOperation.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(
                                      PackedUserOperation calldata userOp,
                                      bytes32 userOpHash,
                                      uint256 missingAccountFunds
                                  ) external returns (uint256 validationData);
                              }
                              // SPDX-License-Identifier: GPL-3.0
                              pragma solidity >=0.7.5;
                              import "./PackedUserOperation.sol";
                              interface IAccountExecute {
                                  /**
                                   * Account may implement this execute method.
                                   * passing this methodSig at the beginning of callData will cause the entryPoint to pass the full UserOp (and hash)
                                   * to the account.
                                   * The account should skip the methodSig, and use the callData (and optionally, other UserOp fields)
                                   *
                                   * @param userOp              - The operation that was just validated.
                                   * @param userOpHash          - Hash of the user's request data.
                                   */
                                  function executeUserOp(
                                      PackedUserOperation calldata userOp,
                                      bytes32 userOpHash
                                  ) external;
                              }
                              // SPDX-License-Identifier: GPL-3.0
                              pragma solidity >=0.7.5;
                              import "./PackedUserOperation.sol";
                              /**
                               * Aggregated Signatures validator.
                               */
                              interface IAggregator {
                                  /**
                                   * Validate aggregated signature.
                                   * Revert if the aggregated signature does not match the given list of operations.
                                   * @param userOps   - Array of UserOperations to validate the signature for.
                                   * @param signature - The aggregated signature.
                                   */
                                  function validateSignatures(
                                      PackedUserOperation[] calldata userOps,
                                      bytes calldata signature
                                  ) external view;
                                  /**
                                   * Validate signature of a single userOp.
                                   * This method should be called by bundler after EntryPointSimulation.simulateValidation() returns
                                   * the aggregator this account uses.
                                   * 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(
                                      PackedUserOperation 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(
                                      PackedUserOperation[] 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.7.5;
                              /* solhint-disable avoid-low-level-calls */
                              /* solhint-disable no-inline-assembly */
                              /* solhint-disable reason-string */
                              import "./PackedUserOperation.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 if the UserOperation Paymaster's "postOp" call 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 PostOpRevertReason(
                                      bytes32 indexed userOpHash,
                                      address indexed sender,
                                      uint256 nonce,
                                      bytes revertReason
                                  );
                                  /**
                                   * UserOp consumed more than prefund. The UserOperation is reverted, and no refund is made.
                                   * @param userOpHash   - The request unique identifier.
                                   * @param sender       - The sender of this request.
                                   * @param nonce        - The nonce used in the request.
                                   */
                                  event UserOperationPrefundTooLow(
                                      bytes32 indexed userOpHash,
                                      address indexed sender,
                                      uint256 nonce
                                  );
                                  /**
                                   * 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.
                                   * @param aggregator - The aggregator used for the following UserOperationEvents.
                                   */
                                  event SignatureAggregatorChanged(address indexed aggregator);
                                  /**
                                   * A custom revert error of handleOps, to identify the offending op.
                                   * 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.
                                   * 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.
                                   */
                                  error FailedOp(uint256 opIndex, string reason);
                                  /**
                                   * A custom revert error of handleOps, to report a revert by account or paymaster.
                                   * @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
                                   * @param reason  - Revert reason. see FailedOp(uint256,string), above
                                   * @param inner   - data from inner cought revert reason
                                   * @dev note that inner is truncated to 2048 bytes
                                   */
                                  error FailedOpWithRevert(uint256 opIndex, string reason, bytes inner);
                                  error PostOpReverted(bytes returnData);
                                  /**
                                   * Error case when a signature aggregator fails to verify the aggregated signature it had created.
                                   * @param aggregator The aggregator that failed to verify the signature
                                   */
                                  error SignatureValidationFailed(address aggregator);
                                  // Return value of getSenderAddress.
                                  error SenderAddressResult(address sender);
                                  // UserOps handled, per aggregator.
                                  struct UserOpsPerAggregator {
                                      PackedUserOperation[] userOps;
                                      // Aggregator address
                                      IAggregator aggregator;
                                      // Aggregated signature
                                      bytes signature;
                                  }
                                  /**
                                   * 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(
                                      PackedUserOperation[] 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.
                                   * @param userOp - The user operation to generate the request ID for.
                                   * @return hash the hash of this UserOperation
                                   */
                                  function getUserOpHash(
                                      PackedUserOperation calldata userOp
                                  ) external view returns (bytes32);
                                  /**
                                   * Gas and return values during simulation.
                                   * @param preOpGas         - The gas used for validation (including preValidationGas)
                                   * @param prefund          - The required prefund for this operation
                                   * @param accountValidationData   - returned validationData from account.
                                   * @param paymasterValidationData - return validationData from paymaster.
                                   * @param paymasterContext - Returned by validatePaymasterUserOp (to be passed into postOp)
                                   */
                                  struct ReturnInfo {
                                      uint256 preOpGas;
                                      uint256 prefund;
                                      uint256 accountValidationData;
                                      uint256 paymasterValidationData;
                                      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;
                                  error DelegateAndRevert(bool success, bytes ret);
                                  /**
                                   * Helper method for dry-run testing.
                                   * @dev calling this method, the EntryPoint will make a delegatecall to the given data, and report (via revert) the result.
                                   *  The method always revert, so is only useful off-chain for dry run calls, in cases where state-override to replace
                                   *  actual EntryPoint code is less convenient.
                                   * @param target a target contract to make a delegatecall from entrypoint
                                   * @param data data to pass to target in a delegatecall
                                   */
                                  function delegateAndRevert(address target, bytes calldata data) external;
                              }
                              // SPDX-License-Identifier: GPL-3.0
                              pragma solidity >=0.7.5;
                              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.7.5;
                              import "./PackedUserOperation.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 {
                                      // User op succeeded.
                                      opSucceeded,
                                      // User op reverted. Still has to pay for gas.
                                      opReverted,
                                      // Only used internally in the EntryPoint (cleanup after postOp reverts). Never calling paymaster with this value
                                      postOpReverted
                                  }
                                  /**
                                   * 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,
                                   *                                                    other values are invalid for paymaster.
                                   *                          <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(
                                      PackedUserOperation 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. The paymaster still has to pay for gas.
                                   *                        postOpReverted - never passed in a call to postOp().
                                   * @param context       - The context value returned by validatePaymasterUserOp
                                   * @param actualGasCost - Actual gas used so far (without this postOp call).
                                   * @param actualUserOpFeePerGas - the gas price this UserOp pays. This value is based on the UserOp's maxFeePerGas
                                   *                        and maxPriorityFee (and basefee)
                                   *                        It is not the same as tx.gasprice, which is what the bundler pays.
                                   */
                                  function postOp(
                                      PostOpMode mode,
                                      bytes calldata context,
                                      uint256 actualGasCost,
                                      uint256 actualUserOpFeePerGas
                                  ) external;
                              }
                              // SPDX-License-Identifier: GPL-3.0-only
                              pragma solidity >=0.7.5;
                              /**
                               * 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 fits into one cell (used during handleOp)
                                   *      and the rest fit into a 2nd cell (used during stake/unstake)
                                   *      - 112 bit allows for 10^15 eth
                                   *      - 48 bit for full timestamp
                                   *      - 32 bit allows 150 years for unstake delay
                                   */
                                  struct DepositInfo {
                                      uint256 deposit;
                                      bool staked;
                                      uint112 stake;
                                      uint32 unstakeDelaySec;
                                      uint48 withdrawTime;
                                  }
                                  // API struct used by getStakeInfo and simulateValidation.
                                  struct StakeInfo {
                                      uint256 stake;
                                      uint256 unstakeDelaySec;
                                  }
                                  /**
                                   * Get deposit info.
                                   * @param account - The account to query.
                                   * @return info   - Full deposit information of given account.
                                   */
                                  function getDepositInfo(
                                      address account
                                  ) external view returns (DepositInfo memory info);
                                  /**
                                   * Get account balance.
                                   * @param account - The account to query.
                                   * @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.
                                   * @param account - The account to add to.
                                   */
                                  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.7.5;
                              /**
                               * 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 accountGasLimits      - Packed gas limits for validateUserOp and gas limit passed to the callData method call.
                               * @param preVerificationGas    - Gas not calculated by the handleOps method, but added to the gas paid.
                               *                                Covers batch overhead.
                               * @param gasFees               - packed gas fields maxPriorityFeePerGas and maxFeePerGas - Same as EIP-1559 gas parameters.
                               * @param paymasterAndData      - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra 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 PackedUserOperation {
                                  address sender;
                                  uint256 nonce;
                                  bytes initCode;
                                  bytes callData;
                                  bytes32 accountGasLimits;
                                  uint256 preVerificationGas;
                                  bytes32 gasFees;
                                  bytes paymasterAndData;
                                  bytes signature;
                              }
                              // SPDX-License-Identifier: LGPL-3.0-only
                              pragma solidity ^0.8.23;
                              // 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 ("memory-safe") {
                                          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 ("memory-safe") {
                                          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 ("memory-safe") {
                                          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 ("memory-safe") {
                                          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 ("memory-safe") {
                                          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 6: FiatTokenProxy
                              pragma solidity ^0.4.24;
                              
                              // File: zos-lib/contracts/upgradeability/Proxy.sol
                              
                              /**
                               * @title Proxy
                               * @dev Implements delegation of calls to other contracts, with proper
                               * forwarding of return values and bubbling of failures.
                               * It defines a fallback function that delegates all calls to the address
                               * returned by the abstract _implementation() internal function.
                               */
                              contract Proxy {
                                /**
                                 * @dev Fallback function.
                                 * Implemented entirely in `_fallback`.
                                 */
                                function () payable external {
                                  _fallback();
                                }
                              
                                /**
                                 * @return The Address of the implementation.
                                 */
                                function _implementation() internal view returns (address);
                              
                                /**
                                 * @dev Delegates execution to an implementation contract.
                                 * This is a low level function that doesn't return to its internal call site.
                                 * It will return to the external caller whatever the implementation returns.
                                 * @param implementation Address to delegate.
                                 */
                                function _delegate(address implementation) internal {
                                  assembly {
                                    // Copy msg.data. We take full control of memory in this inline assembly
                                    // block because it will not return to Solidity code. We overwrite the
                                    // Solidity scratch pad at memory position 0.
                                    calldatacopy(0, 0, calldatasize)
                              
                                    // Call the implementation.
                                    // out and outsize are 0 because we don't know the size yet.
                                    let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
                              
                                    // Copy the returned data.
                                    returndatacopy(0, 0, returndatasize)
                              
                                    switch result
                                    // delegatecall returns 0 on error.
                                    case 0 { revert(0, returndatasize) }
                                    default { return(0, returndatasize) }
                                  }
                                }
                              
                                /**
                                 * @dev Function that is run as the first thing in the fallback function.
                                 * Can be redefined in derived contracts to add functionality.
                                 * Redefinitions must call super._willFallback().
                                 */
                                function _willFallback() internal {
                                }
                              
                                /**
                                 * @dev fallback implementation.
                                 * Extracted to enable manual triggering.
                                 */
                                function _fallback() internal {
                                  _willFallback();
                                  _delegate(_implementation());
                                }
                              }
                              
                              // File: openzeppelin-solidity/contracts/AddressUtils.sol
                              
                              /**
                               * Utility library of inline functions on addresses
                               */
                              library AddressUtils {
                              
                                /**
                                 * Returns whether the target address is a contract
                                 * @dev This function will return false if invoked during the constructor of a contract,
                                 * as the code is not actually created until after the constructor finishes.
                                 * @param addr address to check
                                 * @return whether the target address is a contract
                                 */
                                function isContract(address addr) internal view returns (bool) {
                                  uint256 size;
                                  // XXX Currently there is no better way to check if there is a contract in an address
                                  // than to check the size of the code at that address.
                                  // See https://ethereum.stackexchange.com/a/14016/36603
                                  // for more details about how this works.
                                  // TODO Check this again before the Serenity release, because all addresses will be
                                  // contracts then.
                                  // solium-disable-next-line security/no-inline-assembly
                                  assembly { size := extcodesize(addr) }
                                  return size > 0;
                                }
                              
                              }
                              
                              // File: zos-lib/contracts/upgradeability/UpgradeabilityProxy.sol
                              
                              /**
                               * @title UpgradeabilityProxy
                               * @dev This contract implements a proxy that allows to change the
                               * implementation address to which it will delegate.
                               * Such a change is called an implementation upgrade.
                               */
                              contract UpgradeabilityProxy is Proxy {
                                /**
                                 * @dev Emitted when the implementation is upgraded.
                                 * @param implementation Address of the new implementation.
                                 */
                                event Upgraded(address implementation);
                              
                                /**
                                 * @dev Storage slot with the address of the current implementation.
                                 * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is
                                 * validated in the constructor.
                                 */
                                bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
                              
                                /**
                                 * @dev Contract constructor.
                                 * @param _implementation Address of the initial implementation.
                                 */
                                constructor(address _implementation) public {
                                  assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation"));
                              
                                  _setImplementation(_implementation);
                                }
                              
                                /**
                                 * @dev Returns the current implementation.
                                 * @return Address of the current implementation
                                 */
                                function _implementation() internal view returns (address impl) {
                                  bytes32 slot = IMPLEMENTATION_SLOT;
                                  assembly {
                                    impl := sload(slot)
                                  }
                                }
                              
                                /**
                                 * @dev Upgrades the proxy to a new implementation.
                                 * @param newImplementation Address of the new implementation.
                                 */
                                function _upgradeTo(address newImplementation) internal {
                                  _setImplementation(newImplementation);
                                  emit Upgraded(newImplementation);
                                }
                              
                                /**
                                 * @dev Sets the implementation address of the proxy.
                                 * @param newImplementation Address of the new implementation.
                                 */
                                function _setImplementation(address newImplementation) private {
                                  require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
                              
                                  bytes32 slot = IMPLEMENTATION_SLOT;
                              
                                  assembly {
                                    sstore(slot, newImplementation)
                                  }
                                }
                              }
                              
                              // File: zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol
                              
                              /**
                               * @title AdminUpgradeabilityProxy
                               * @dev This contract combines an upgradeability proxy with an authorization
                               * mechanism for administrative tasks.
                               * All external functions in this contract must be guarded by the
                               * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
                               * feature proposal that would enable this to be done automatically.
                               */
                              contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
                                /**
                                 * @dev Emitted when the administration has been transferred.
                                 * @param previousAdmin Address of the previous admin.
                                 * @param newAdmin Address of the new admin.
                                 */
                                event AdminChanged(address previousAdmin, address newAdmin);
                              
                                /**
                                 * @dev Storage slot with the admin of the contract.
                                 * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is
                                 * validated in the constructor.
                                 */
                                bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b;
                              
                                /**
                                 * @dev Modifier to check whether the `msg.sender` is the admin.
                                 * If it is, it will run the function. Otherwise, it will delegate the call
                                 * to the implementation.
                                 */
                                modifier ifAdmin() {
                                  if (msg.sender == _admin()) {
                                    _;
                                  } else {
                                    _fallback();
                                  }
                                }
                              
                                /**
                                 * Contract constructor.
                                 * It sets the `msg.sender` as the proxy administrator.
                                 * @param _implementation address of the initial implementation.
                                 */
                                constructor(address _implementation) UpgradeabilityProxy(_implementation) public {
                                  assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));
                              
                                  _setAdmin(msg.sender);
                                }
                              
                                /**
                                 * @return The address of the proxy admin.
                                 */
                                function admin() external view ifAdmin returns (address) {
                                  return _admin();
                                }
                              
                                /**
                                 * @return The address of the implementation.
                                 */
                                function implementation() external view ifAdmin returns (address) {
                                  return _implementation();
                                }
                              
                                /**
                                 * @dev Changes the admin of the proxy.
                                 * Only the current admin can call this function.
                                 * @param newAdmin Address to transfer proxy administration to.
                                 */
                                function changeAdmin(address newAdmin) external ifAdmin {
                                  require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
                                  emit AdminChanged(_admin(), newAdmin);
                                  _setAdmin(newAdmin);
                                }
                              
                                /**
                                 * @dev Upgrade the backing implementation of the proxy.
                                 * Only the admin can call this function.
                                 * @param newImplementation Address of the new implementation.
                                 */
                                function upgradeTo(address newImplementation) external ifAdmin {
                                  _upgradeTo(newImplementation);
                                }
                              
                                /**
                                 * @dev Upgrade the backing implementation of the proxy and call a function
                                 * on the new implementation.
                                 * This is useful to initialize the proxied contract.
                                 * @param newImplementation Address of the new implementation.
                                 * @param data Data to send as msg.data in the low level call.
                                 * It should include the signature and the parameters of the function to be
                                 * called, as described in
                                 * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding.
                                 */
                                function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin {
                                  _upgradeTo(newImplementation);
                                  require(address(this).call.value(msg.value)(data));
                                }
                              
                                /**
                                 * @return The admin slot.
                                 */
                                function _admin() internal view returns (address adm) {
                                  bytes32 slot = ADMIN_SLOT;
                                  assembly {
                                    adm := sload(slot)
                                  }
                                }
                              
                                /**
                                 * @dev Sets the address of the proxy admin.
                                 * @param newAdmin Address of the new proxy admin.
                                 */
                                function _setAdmin(address newAdmin) internal {
                                  bytes32 slot = ADMIN_SLOT;
                              
                                  assembly {
                                    sstore(slot, newAdmin)
                                  }
                                }
                              
                                /**
                                 * @dev Only fall back when the sender is not the admin.
                                 */
                                function _willFallback() internal {
                                  require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
                                  super._willFallback();
                                }
                              }
                              
                              // File: contracts/FiatTokenProxy.sol
                              
                              /**
                              * Copyright CENTRE SECZ 2018
                              *
                              * Permission is hereby granted, free of charge, to any person obtaining a copy 
                              * of this software and associated documentation files (the "Software"), to deal 
                              * in the Software without restriction, including without limitation the rights 
                              * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
                              * copies of the Software, and to permit persons to whom the Software is furnished to 
                              * do so, subject to the following conditions:
                              *
                              * The above copyright notice and this permission notice shall be included in all 
                              * copies or substantial portions of the Software.
                              *
                              * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
                              * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
                              * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
                              * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
                              * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
                              * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
                              */
                              
                              pragma solidity ^0.4.24;
                              
                              
                              /**
                               * @title FiatTokenProxy
                               * @dev This contract proxies FiatToken calls and enables FiatToken upgrades
                              */ 
                              contract FiatTokenProxy is AdminUpgradeabilityProxy {
                                  constructor(address _implementation) public AdminUpgradeabilityProxy(_implementation) {
                                  }
                              }

                              File 3 of 6: LiFiDiamond
                              // SPDX-License-Identifier: MIT
                              pragma solidity 0.8.17;
                              error TokenAddressIsZero();
                              error TokenNotSupported();
                              error CannotBridgeToSameNetwork();
                              error ZeroPostSwapBalance();
                              error NoSwapDataProvided();
                              error NativeValueWithERC();
                              error ContractCallNotAllowed();
                              error NullAddrIsNotAValidSpender();
                              error NullAddrIsNotAnERC20Token();
                              error NoTransferToNullAddress();
                              error NativeAssetTransferFailed();
                              error InvalidBridgeConfigLength();
                              error InvalidAmount();
                              error InvalidContract();
                              error InvalidConfig();
                              error UnsupportedChainId(uint256 chainId);
                              error InvalidReceiver();
                              error InvalidDestinationChain();
                              error InvalidSendingToken();
                              error InvalidCaller();
                              error AlreadyInitialized();
                              error NotInitialized();
                              error OnlyContractOwner();
                              error CannotAuthoriseSelf();
                              error RecoveryAddressCannotBeZero();
                              error CannotDepositNativeToken();
                              error InvalidCallData();
                              error NativeAssetNotSupported();
                              error UnAuthorized();
                              error NoSwapFromZeroBalance();
                              error InvalidFallbackAddress();
                              error CumulativeSlippageTooHigh(uint256 minAmount, uint256 receivedAmount);
                              error InsufficientBalance(uint256 required, uint256 balance);
                              error ZeroAmount();
                              error InvalidFee();
                              error InformationMismatch();
                              error NotAContract();
                              error NotEnoughBalance(uint256 requested, uint256 available);
                              // SPDX-License-Identifier: MIT
                              pragma solidity 0.8.17;
                              interface IDiamondCut {
                                  enum FacetCutAction {
                                      Add,
                                      Replace,
                                      Remove
                                  }
                                  // Add=0, Replace=1, Remove=2
                                  struct FacetCut {
                                      address facetAddress;
                                      FacetCutAction action;
                                      bytes4[] functionSelectors;
                                  }
                                  /// @notice Add/replace/remove any number of functions and optionally execute
                                  ///         a function with delegatecall
                                  /// @param _diamondCut Contains the facet addresses and function selectors
                                  /// @param _init The address of the contract or facet to execute _calldata
                                  /// @param _calldata A function call, including function selector and arguments
                                  ///                  _calldata is executed with delegatecall on _init
                                  function diamondCut(
                                      FacetCut[] calldata _diamondCut,
                                      address _init,
                                      bytes calldata _calldata
                                  ) external;
                                  event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
                              }
                              // SPDX-License-Identifier: MIT
                              pragma solidity 0.8.17;
                              import { LibDiamond } from "./Libraries/LibDiamond.sol";
                              import { IDiamondCut } from "./Interfaces/IDiamondCut.sol";
                              import { LibUtil } from "./Libraries/LibUtil.sol";
                              contract LiFiDiamond {
                                  constructor(address _contractOwner, address _diamondCutFacet) payable {
                                      LibDiamond.setContractOwner(_contractOwner);
                                      // Add the diamondCut external function from the diamondCutFacet
                                      IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
                                      bytes4[] memory functionSelectors = new bytes4[](1);
                                      functionSelectors[0] = IDiamondCut.diamondCut.selector;
                                      cut[0] = IDiamondCut.FacetCut({
                                          facetAddress: _diamondCutFacet,
                                          action: IDiamondCut.FacetCutAction.Add,
                                          functionSelectors: functionSelectors
                                      });
                                      LibDiamond.diamondCut(cut, address(0), "");
                                  }
                                  // Find facet for function that is called and execute the
                                  // function if a facet is found and return any value.
                                  // solhint-disable-next-line no-complex-fallback
                                  fallback() external payable {
                                      LibDiamond.DiamondStorage storage ds;
                                      bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
                                      // get diamond storage
                                      // solhint-disable-next-line no-inline-assembly
                                      assembly {
                                          ds.slot := position
                                      }
                                      // get facet from function selector
                                      address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
                                      if (facet == address(0)) {
                                          revert LibDiamond.FunctionDoesNotExist();
                                      }
                                      // Execute external function from facet using delegatecall and return any value.
                                      // solhint-disable-next-line no-inline-assembly
                                      assembly {
                                          // copy function selector and any arguments
                                          calldatacopy(0, 0, calldatasize())
                                          // execute function call using the facet
                                          let result := delegatecall(gas(), facet, 0, calldatasize(), 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())
                                          }
                                      }
                                  }
                                  // Able to receive ether
                                  // solhint-disable-next-line no-empty-blocks
                                  receive() external payable {}
                              }
                              // SPDX-License-Identifier: MIT
                              pragma solidity 0.8.17;
                              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 concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
                                      assembly {
                                          // Read the first 32 bytes of _preBytes storage, which is the length
                                          // of the array. (We don't need to use the offset into the slot
                                          // because arrays use the entire slot.)
                                          let fslot := sload(_preBytes.slot)
                                          // Arrays of 31 bytes or less have an even value in their slot,
                                          // while longer arrays have an odd value. The actual length is
                                          // the slot divided by two for odd values, and the lowest order
                                          // byte divided by two for even values.
                                          // If the slot is even, bitwise and the slot with 255 and divide by
                                          // two to get the length. If the slot is odd, bitwise and the slot
                                          // with -1 and divide by two.
                                          let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                                          let mlength := mload(_postBytes)
                                          let newlength := add(slength, mlength)
                                          // slength can contain both the length and contents of the array
                                          // if length < 32 bytes so let's prepare for that
                                          // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                                          switch add(lt(slength, 32), lt(newlength, 32))
                                          case 2 {
                                              // Since the new array still fits in the slot, we just need to
                                              // update the contents of the slot.
                                              // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                                              sstore(
                                                  _preBytes.slot,
                                                  // all the modifications to the slot are inside this
                                                  // next block
                                                  add(
                                                      // we can just add to the slot contents because the
                                                      // bytes we want to change are the LSBs
                                                      fslot,
                                                      add(
                                                          mul(
                                                              div(
                                                                  // load the bytes from memory
                                                                  mload(add(_postBytes, 0x20)),
                                                                  // zero all bytes to the right
                                                                  exp(0x100, sub(32, mlength))
                                                              ),
                                                              // and now shift left the number of bytes to
                                                              // leave space for the length in the slot
                                                              exp(0x100, sub(32, newlength))
                                                          ),
                                                          // increase length by the double of the memory
                                                          // bytes length
                                                          mul(mlength, 2)
                                                      )
                                                  )
                                              )
                                          }
                                          case 1 {
                                              // The stored value fits in the slot, but the combined value
                                              // will exceed it.
                                              // get the keccak hash to get the contents of the array
                                              mstore(0x0, _preBytes.slot)
                                              let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                                              // save new length
                                              sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                                              // The contents of the _postBytes array start 32 bytes into
                                              // the structure. Our first read should obtain the `submod`
                                              // bytes that can fit into the unused space in the last word
                                              // of the stored array. To get this, we read 32 bytes starting
                                              // from `submod`, so the data we read overlaps with the array
                                              // contents by `submod` bytes. Masking the lowest-order
                                              // `submod` bytes allows us to add that value directly to the
                                              // stored value.
                                              let submod := sub(32, slength)
                                              let mc := add(_postBytes, submod)
                                              let end := add(_postBytes, mlength)
                                              let mask := sub(exp(0x100, submod), 1)
                                              sstore(
                                                  sc,
                                                  add(
                                                      and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00),
                                                      and(mload(mc), mask)
                                                  )
                                              )
                                              for {
                                                  mc := add(mc, 0x20)
                                                  sc := add(sc, 1)
                                              } lt(mc, end) {
                                                  sc := add(sc, 1)
                                                  mc := add(mc, 0x20)
                                              } {
                                                  sstore(sc, mload(mc))
                                              }
                                              mask := exp(0x100, sub(mc, end))
                                              sstore(sc, mul(div(mload(mc), mask), mask))
                                          }
                                          default {
                                              // get the keccak hash to get the contents of the array
                                              mstore(0x0, _preBytes.slot)
                                              // Start copying to the last used word of the stored array.
                                              let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                                              // save new length
                                              sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                                              // Copy over the first `submod` bytes of the new data as in
                                              // case 1 above.
                                              let slengthmod := mod(slength, 32)
                                              let submod := sub(32, slengthmod)
                                              let mc := add(_postBytes, submod)
                                              let end := add(_postBytes, mlength)
                                              let mask := sub(exp(0x100, submod), 1)
                                              sstore(sc, add(sload(sc), and(mload(mc), mask)))
                                              for {
                                                  sc := add(sc, 1)
                                                  mc := add(mc, 0x20)
                                              } lt(mc, end) {
                                                  sc := add(sc, 1)
                                                  mc := add(mc, 0x20)
                                              } {
                                                  sstore(sc, mload(mc))
                                              }
                                              mask := exp(0x100, sub(mc, end))
                                              sstore(sc, mul(div(mload(mc), mask), mask))
                                          }
                                      }
                                  }
                                  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;
                                  }
                                  function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
                                      if (_bytes.length < _start + 20) {
                                          revert AddressOutOfBounds();
                                      }
                                      address tempAddress;
                                      assembly {
                                          tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
                                      }
                                      return tempAddress;
                                  }
                                  function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
                                      if (_bytes.length < _start + 1) {
                                          revert UintOutOfBounds();
                                      }
                                      uint8 tempUint;
                                      assembly {
                                          tempUint := mload(add(add(_bytes, 0x1), _start))
                                      }
                                      return tempUint;
                                  }
                                  function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
                                      if (_bytes.length < _start + 2) {
                                          revert UintOutOfBounds();
                                      }
                                      uint16 tempUint;
                                      assembly {
                                          tempUint := mload(add(add(_bytes, 0x2), _start))
                                      }
                                      return tempUint;
                                  }
                                  function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
                                      if (_bytes.length < _start + 4) {
                                          revert UintOutOfBounds();
                                      }
                                      uint32 tempUint;
                                      assembly {
                                          tempUint := mload(add(add(_bytes, 0x4), _start))
                                      }
                                      return tempUint;
                                  }
                                  function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
                                      if (_bytes.length < _start + 8) {
                                          revert UintOutOfBounds();
                                      }
                                      uint64 tempUint;
                                      assembly {
                                          tempUint := mload(add(add(_bytes, 0x8), _start))
                                      }
                                      return tempUint;
                                  }
                                  function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
                                      if (_bytes.length < _start + 12) {
                                          revert UintOutOfBounds();
                                      }
                                      uint96 tempUint;
                                      assembly {
                                          tempUint := mload(add(add(_bytes, 0xc), _start))
                                      }
                                      return tempUint;
                                  }
                                  function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
                                      if (_bytes.length < _start + 16) {
                                          revert UintOutOfBounds();
                                      }
                                      uint128 tempUint;
                                      assembly {
                                          tempUint := mload(add(add(_bytes, 0x10), _start))
                                      }
                                      return tempUint;
                                  }
                                  function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
                                      if (_bytes.length < _start + 32) {
                                          revert UintOutOfBounds();
                                      }
                                      uint256 tempUint;
                                      assembly {
                                          tempUint := mload(add(add(_bytes, 0x20), _start))
                                      }
                                      return tempUint;
                                  }
                                  function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
                                      if (_bytes.length < _start + 32) {
                                          revert UintOutOfBounds();
                                      }
                                      bytes32 tempBytes32;
                                      assembly {
                                          tempBytes32 := mload(add(add(_bytes, 0x20), _start))
                                      }
                                      return tempBytes32;
                                  }
                                  function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
                                      bool success = true;
                                      assembly {
                                          let length := mload(_preBytes)
                                          // if lengths don't match the arrays are not equal
                                          switch eq(length, mload(_postBytes))
                                          case 1 {
                                              // cb is a circuit breaker in the for loop since there's
                                              //  no said feature for inline assembly loops
                                              // cb = 1 - don't breaker
                                              // cb = 0 - break
                                              let cb := 1
                                              let mc := add(_preBytes, 0x20)
                                              let end := add(mc, length)
                                              for {
                                                  let cc := add(_postBytes, 0x20)
                                                  // the next line is the loop condition:
                                                  // while(uint256(mc < end) + cb == 2)
                                              } eq(add(lt(mc, end), cb), 2) {
                                                  mc := add(mc, 0x20)
                                                  cc := add(cc, 0x20)
                                              } {
                                                  // if any of these checks fails then arrays are not equal
                                                  if iszero(eq(mload(mc), mload(cc))) {
                                                      // unsuccess:
                                                      success := 0
                                                      cb := 0
                                                  }
                                              }
                                          }
                                          default {
                                              // unsuccess:
                                              success := 0
                                          }
                                      }
                                      return success;
                                  }
                                  function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) {
                                      bool success = true;
                                      assembly {
                                          // we know _preBytes_offset is 0
                                          let fslot := sload(_preBytes.slot)
                                          // Decode the length of the stored array like in concatStorage().
                                          let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                                          let mlength := mload(_postBytes)
                                          // if lengths don't match the arrays are not equal
                                          switch eq(slength, mlength)
                                          case 1 {
                                              // slength can contain both the length and contents of the array
                                              // if length < 32 bytes so let's prepare for that
                                              // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                                              if iszero(iszero(slength)) {
                                                  switch lt(slength, 32)
                                                  case 1 {
                                                      // blank the last byte which is the length
                                                      fslot := mul(div(fslot, 0x100), 0x100)
                                                      if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                                                          // unsuccess:
                                                          success := 0
                                                      }
                                                  }
                                                  default {
                                                      // cb is a circuit breaker in the for loop since there's
                                                      //  no said feature for inline assembly loops
                                                      // cb = 1 - don't breaker
                                                      // cb = 0 - break
                                                      let cb := 1
                                                      // get the keccak hash to get the contents of the array
                                                      mstore(0x0, _preBytes.slot)
                                                      let sc := keccak256(0x0, 0x20)
                                                      let mc := add(_postBytes, 0x20)
                                                      let end := add(mc, mlength)
                                                      // the next line is the loop condition:
                                                      // while(uint256(mc < end) + cb == 2)
                                                      // solhint-disable-next-line no-empty-blocks
                                                      for {
                                                      } eq(add(lt(mc, end), cb), 2) {
                                                          sc := add(sc, 1)
                                                          mc := add(mc, 0x20)
                                                      } {
                                                          if iszero(eq(sload(sc), mload(mc))) {
                                                              // unsuccess:
                                                              success := 0
                                                              cb := 0
                                                          }
                                                      }
                                                  }
                                              }
                                          }
                                          default {
                                              // unsuccess:
                                              success := 0
                                          }
                                      }
                                      return success;
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              pragma solidity 0.8.17;
                              import { IDiamondCut } from "../Interfaces/IDiamondCut.sol";
                              import { LibUtil } from "../Libraries/LibUtil.sol";
                              import { OnlyContractOwner } from "../Errors/GenericErrors.sol";
                              /// Implementation of EIP-2535 Diamond Standard
                              /// https://eips.ethereum.org/EIPS/eip-2535
                              library LibDiamond {
                                  bytes32 internal constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");
                                  // Diamond specific errors
                                  error IncorrectFacetCutAction();
                                  error NoSelectorsInFace();
                                  error FunctionAlreadyExists();
                                  error FacetAddressIsZero();
                                  error FacetAddressIsNotZero();
                                  error FacetContainsNoCode();
                                  error FunctionDoesNotExist();
                                  error FunctionIsImmutable();
                                  error InitZeroButCalldataNotEmpty();
                                  error CalldataEmptyButInitNotZero();
                                  error InitReverted();
                                  // ----------------
                                  struct FacetAddressAndPosition {
                                      address facetAddress;
                                      uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
                                  }
                                  struct FacetFunctionSelectors {
                                      bytes4[] functionSelectors;
                                      uint256 facetAddressPosition; // position of facetAddress in facetAddresses array
                                  }
                                  struct DiamondStorage {
                                      // maps function selector to the facet address and
                                      // the position of the selector in the facetFunctionSelectors.selectors array
                                      mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
                                      // maps facet addresses to function selectors
                                      mapping(address => FacetFunctionSelectors) facetFunctionSelectors;
                                      // facet addresses
                                      address[] facetAddresses;
                                      // Used to query if a contract implements an interface.
                                      // Used to implement ERC-165.
                                      mapping(bytes4 => bool) supportedInterfaces;
                                      // owner of the contract
                                      address contractOwner;
                                  }
                                  function diamondStorage() internal pure returns (DiamondStorage storage ds) {
                                      bytes32 position = DIAMOND_STORAGE_POSITION;
                                      // solhint-disable-next-line no-inline-assembly
                                      assembly {
                                          ds.slot := position
                                      }
                                  }
                                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                                  function setContractOwner(address _newOwner) internal {
                                      DiamondStorage storage ds = diamondStorage();
                                      address previousOwner = ds.contractOwner;
                                      ds.contractOwner = _newOwner;
                                      emit OwnershipTransferred(previousOwner, _newOwner);
                                  }
                                  function contractOwner() internal view returns (address contractOwner_) {
                                      contractOwner_ = diamondStorage().contractOwner;
                                  }
                                  function enforceIsContractOwner() internal view {
                                      if (msg.sender != diamondStorage().contractOwner) revert OnlyContractOwner();
                                  }
                                  event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);
                                  // Internal function version of diamondCut
                                  function diamondCut(
                                      IDiamondCut.FacetCut[] memory _diamondCut,
                                      address _init,
                                      bytes memory _calldata
                                  ) internal {
                                      for (uint256 facetIndex; facetIndex < _diamondCut.length; ) {
                                          IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
                                          if (action == IDiamondCut.FacetCutAction.Add) {
                                              addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
                                          } else if (action == IDiamondCut.FacetCutAction.Replace) {
                                              replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
                                          } else if (action == IDiamondCut.FacetCutAction.Remove) {
                                              removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
                                          } else {
                                              revert IncorrectFacetCutAction();
                                          }
                                          unchecked {
                                              ++facetIndex;
                                          }
                                      }
                                      emit DiamondCut(_diamondCut, _init, _calldata);
                                      initializeDiamondCut(_init, _calldata);
                                  }
                                  function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
                                      if (_functionSelectors.length == 0) {
                                          revert NoSelectorsInFace();
                                      }
                                      DiamondStorage storage ds = diamondStorage();
                                      if (LibUtil.isZeroAddress(_facetAddress)) {
                                          revert FacetAddressIsZero();
                                      }
                                      uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
                                      // add new facet address if it does not exist
                                      if (selectorPosition == 0) {
                                          addFacet(ds, _facetAddress);
                                      }
                                      for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
                                          bytes4 selector = _functionSelectors[selectorIndex];
                                          address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
                                          if (!LibUtil.isZeroAddress(oldFacetAddress)) {
                                              revert FunctionAlreadyExists();
                                          }
                                          addFunction(ds, selector, selectorPosition, _facetAddress);
                                          unchecked {
                                              ++selectorPosition;
                                              ++selectorIndex;
                                          }
                                      }
                                  }
                                  function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
                                      if (_functionSelectors.length == 0) {
                                          revert NoSelectorsInFace();
                                      }
                                      DiamondStorage storage ds = diamondStorage();
                                      if (LibUtil.isZeroAddress(_facetAddress)) {
                                          revert FacetAddressIsZero();
                                      }
                                      uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
                                      // add new facet address if it does not exist
                                      if (selectorPosition == 0) {
                                          addFacet(ds, _facetAddress);
                                      }
                                      for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
                                          bytes4 selector = _functionSelectors[selectorIndex];
                                          address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
                                          if (oldFacetAddress == _facetAddress) {
                                              revert FunctionAlreadyExists();
                                          }
                                          removeFunction(ds, oldFacetAddress, selector);
                                          addFunction(ds, selector, selectorPosition, _facetAddress);
                                          unchecked {
                                              ++selectorPosition;
                                              ++selectorIndex;
                                          }
                                      }
                                  }
                                  function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
                                      if (_functionSelectors.length == 0) {
                                          revert NoSelectorsInFace();
                                      }
                                      DiamondStorage storage ds = diamondStorage();
                                      // if function does not exist then do nothing and return
                                      if (!LibUtil.isZeroAddress(_facetAddress)) {
                                          revert FacetAddressIsNotZero();
                                      }
                                      for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
                                          bytes4 selector = _functionSelectors[selectorIndex];
                                          address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
                                          removeFunction(ds, oldFacetAddress, selector);
                                          unchecked {
                                              ++selectorIndex;
                                          }
                                      }
                                  }
                                  function addFacet(DiamondStorage storage ds, address _facetAddress) internal {
                                      enforceHasContractCode(_facetAddress);
                                      ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length;
                                      ds.facetAddresses.push(_facetAddress);
                                  }
                                  function addFunction(
                                      DiamondStorage storage ds,
                                      bytes4 _selector,
                                      uint96 _selectorPosition,
                                      address _facetAddress
                                  ) internal {
                                      ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition;
                                      ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector);
                                      ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;
                                  }
                                  function removeFunction(
                                      DiamondStorage storage ds,
                                      address _facetAddress,
                                      bytes4 _selector
                                  ) internal {
                                      if (LibUtil.isZeroAddress(_facetAddress)) {
                                          revert FunctionDoesNotExist();
                                      }
                                      // an immutable function is a function defined directly in a diamond
                                      if (_facetAddress == address(this)) {
                                          revert FunctionIsImmutable();
                                      }
                                      // replace selector with last selector, then delete last selector
                                      uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition;
                                      uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1;
                                      // if not the same then replace _selector with lastSelector
                                      if (selectorPosition != lastSelectorPosition) {
                                          bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition];
                                          ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector;
                                          ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition);
                                      }
                                      // delete the last selector
                                      ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
                                      delete ds.selectorToFacetAndPosition[_selector];
                                      // if no more selectors for facet address then delete the facet address
                                      if (lastSelectorPosition == 0) {
                                          // replace facet address with last facet address and delete last facet address
                                          uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;
                                          uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
                                          if (facetAddressPosition != lastFacetAddressPosition) {
                                              address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition];
                                              ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
                                              ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition;
                                          }
                                          ds.facetAddresses.pop();
                                          delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
                                      }
                                  }
                                  function initializeDiamondCut(address _init, bytes memory _calldata) internal {
                                      if (LibUtil.isZeroAddress(_init)) {
                                          if (_calldata.length != 0) {
                                              revert InitZeroButCalldataNotEmpty();
                                          }
                                      } else {
                                          if (_calldata.length == 0) {
                                              revert CalldataEmptyButInitNotZero();
                                          }
                                          if (_init != address(this)) {
                                              enforceHasContractCode(_init);
                                          }
                                          // solhint-disable-next-line avoid-low-level-calls
                                          (bool success, bytes memory error) = _init.delegatecall(_calldata);
                                          if (!success) {
                                              if (error.length > 0) {
                                                  // bubble up the error
                                                  revert(string(error));
                                              } else {
                                                  revert InitReverted();
                                              }
                                          }
                                      }
                                  }
                                  function enforceHasContractCode(address _contract) internal view {
                                      uint256 contractSize;
                                      // solhint-disable-next-line no-inline-assembly
                                      assembly {
                                          contractSize := extcodesize(_contract)
                                      }
                                      if (contractSize == 0) {
                                          revert FacetContainsNoCode();
                                      }
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              pragma solidity 0.8.17;
                              import "./LibBytes.sol";
                              library LibUtil {
                                  using LibBytes for bytes;
                                  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
                                  }
                                  /// @notice Determines whether the given address is the zero address
                                  /// @param addr The address to verify
                                  /// @return Boolean indicating if the address is the zero address
                                  function isZeroAddress(address addr) internal pure returns (bool) {
                                      return addr == address(0);
                                  }
                              }
                              

                              File 4 of 6: AmbirePaymaster
                              // SPDX-License-Identifier: agpl-3.0
                              pragma solidity 0.8.19;
                              import './libs/SignatureValidator.sol';
                              import './ExternalSigValidator.sol';
                              import './libs/erc4337/PackedUserOperation.sol';
                              import './libs/erc4337/UserOpHelper.sol';
                              import './deployless/IAmbireAccount.sol';
                              /**
                               * @notice  A validator that performs DKIM signature recovery
                               * @dev     All external/public functions (that are not view/pure) use `payable` because AmbireAccount
                               * is a wallet contract, and any ETH sent to it is not lost, but on the other hand not having `payable`
                               * makes the Solidity compiler add an extra check for `msg.value`, which in this case is wasted gas
                               */
                              contract AmbireAccount is IAmbireAccount {
                              \t// @dev We do not have a constructor. This contract cannot be initialized with any valid `privileges` by itself!
                              \t// The intended use case is to deploy one base implementation contract, and create a minimal proxy for each user wallet, by
                              \t// using our own code generation to insert SSTOREs to initialize `privileges` (it was previously called IdentityProxyDeploy.js, now src/libs/proxyDeploy/deploy.ts)
                              \taddress private constant FALLBACK_HANDLER_SLOT = address(0x6969);
                              \t// @dev This is how we understand if msg.sender is the entry point
                              \tbytes32 private constant ENTRY_POINT_MARKER = 0x0000000000000000000000000000000000000000000000000000000000007171;
                              \t// Externally validated signatures
                              \tuint8 private constant SIGMODE_EXTERNALLY_VALIDATED = 255;
                              \t// Variables
                              \tmapping(address => bytes32) public privileges;
                              \tuint256 public nonce;
                              \t// Events
                              \tevent LogPrivilegeChanged(address indexed addr, bytes32 priv);
                              \tevent LogErr(address indexed to, uint256 value, bytes data, bytes returnData); // only used in tryCatch
                              \t// This contract can accept ETH without calldata
                              \treceive() external payable {}
                              \t/**
                              \t * @dev     To support EIP 721 and EIP 1155, we need to respond to those methods with their own method signature
                              \t * @return  bytes4  onERC721Received function selector
                              \t */
                              \tfunction onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
                              \t\treturn this.onERC721Received.selector;
                              \t}
                              \t/**
                              \t * @dev     To support EIP 721 and EIP 1155, we need to respond to those methods with their own method signature
                              \t * @return  bytes4  onERC1155Received function selector
                              \t */
                              \tfunction onERC1155Received(address, address, uint256, uint256, bytes calldata) external pure returns (bytes4) {
                              \t\treturn this.onERC1155Received.selector;
                              \t}
                              \t/**
                              \t * @dev     To support EIP 721 and EIP 1155, we need to respond to those methods with their own method signature
                              \t * @return  bytes4  onERC1155Received function selector
                              \t */
                              \tfunction onERC1155BatchReceived(
                              \t\taddress,
                              \t\taddress,
                              \t\tuint256[] calldata,
                              \t\tuint256[] calldata,
                              \t\tbytes calldata
                              \t) external pure returns (bytes4) {
                              \t\treturn this.onERC1155BatchReceived.selector;
                              \t}
                              \t/**
                              \t * @notice  fallback method: currently used to call the fallback handler
                              \t * which is set by the user and can be changed
                              \t * @dev     this contract can accept ETH with calldata, hence payable
                              \t */
                              \tfallback() external payable {
                              \t\t// We store the fallback handler at this magic slot
                              \t\taddress fallbackHandler = address(uint160(uint(privileges[FALLBACK_HANDLER_SLOT])));
                              \t\tif (fallbackHandler == address(0)) return;
                              \t\tassembly {
                              \t\t\t// we can use addr 0 because logic is taking full control of the
                              \t\t\t// execution making sure it returns itself and does not
                              \t\t\t// rely on any further Solidity code.
                              \t\t\tcalldatacopy(0, 0, calldatasize())
                              \t\t\tlet result := delegatecall(gas(), fallbackHandler, 0, calldatasize(), 0, 0)
                              \t\t\tlet size := returndatasize()
                              \t\t\treturndatacopy(0, 0, size)
                              \t\t\tif eq(result, 0) {
                              \t\t\t\trevert(0, size)
                              \t\t\t}
                              \t\t\treturn(0, size)
                              \t\t}
                              \t}
                              \t/**
                              \t * @notice  used to set the privilege of a key (by `addr`)
                              \t * @dev     normal signatures will be considered valid if the
                              \t * `addr` they are signed with has non-zero (not 0x000..000) privilege set; we can set the privilege to
                              \t * a hash of the recovery keys and timelock (see `RecoveryInfo`) to enable recovery signatures
                              \t * @param   addr  the address to give privs to
                              \t * @param   priv  the privs to give
                              \t */
                              \tfunction setAddrPrivilege(address addr, bytes32 priv) external payable {
                              \t\trequire(msg.sender == address(this), 'ONLY_ACCOUNT_CAN_CALL');
                              \t\tprivileges[addr] = priv;
                              \t\temit LogPrivilegeChanged(addr, priv);
                              \t}
                              \t/**
                              \t * @notice  Useful when we need to do multiple operations but ignore failures in some of them
                              \t * @param   to  address we're sending value to
                              \t * @param   value  the amount
                              \t * @param   data  callData
                              \t */
                              \tfunction tryCatch(address to, uint256 value, bytes calldata data) external payable {
                              \t\trequire(msg.sender == address(this), 'ONLY_ACCOUNT_CAN_CALL');
                              \t\tuint256 gasBefore = gasleft();
                              \t\t(bool success, bytes memory returnData) = to.call{ value: value, gas: gasBefore }(data);
                              \t\trequire(gasleft() > gasBefore / 64, 'TRYCATCH_OOG');
                              \t\tif (!success) emit LogErr(to, value, data, returnData);
                              \t}
                              \t/**
                              \t * @notice  same as `tryCatch` but with a gas limit
                              \t * @param   to  address we're sending value to
                              \t * @param   value  the amount
                              \t * @param   data  callData
                              \t * @param   gasLimit  how much gas is allowed
                              \t */
                              \tfunction tryCatchLimit(address to, uint256 value, bytes calldata data, uint256 gasLimit) external payable {
                              \t\trequire(msg.sender == address(this), 'ONLY_ACCOUNT_CAN_CALL');
                              \t\tuint256 gasBefore = gasleft();
                              \t\t(bool success, bytes memory returnData) = to.call{ value: value, gas: gasLimit }(data);
                              \t\trequire(gasleft() > gasBefore / 64, 'TRYCATCH_OOG');
                              \t\tif (!success) emit LogErr(to, value, data, returnData);
                              \t}
                              \t/**
                              \t * @notice  execute: this method is used to execute a single bundle of calls that are signed with a key
                              \t * that is authorized to execute on this account (in `privileges`)
                              \t * @dev     WARNING: if the signature of this is changed, we have to change AmbireAccountFactory
                              \t * @param   calls  the transaction we're executing. They may not execute
                              \t * if specific cases. One such is when setting a timelock
                              \t * @param   signature  the signature for the transactions
                              \t */
                              \tfunction execute(Transaction[] calldata calls, bytes calldata signature) public payable {
                              \t\taddress signerKey;
                              \t\tuint8 sigMode = uint8(signature[signature.length - 1]);
                              \t\tuint256 currentNonce = nonce;
                              \t\t// we increment the nonce here (not using `nonce++` to save some gas)
                              \t\tnonce = currentNonce + 1;
                              \t\tif (sigMode == SIGMODE_EXTERNALLY_VALIDATED) {
                              \t\t\tbool isValidSig;
                              \t\t\tuint256 timestampValidAfter;
                              \t\t\t(signerKey, isValidSig, timestampValidAfter) = validateExternalSig(calls, signature);
                              \t\t\tif (!isValidSig) {
                              \t\t\t\trequire(block.timestamp >= timestampValidAfter, 'SIGNATURE_VALIDATION_TIMELOCK');
                              \t\t\t\trevert('SIGNATURE_VALIDATION_FAIL');
                              \t\t\t}
                              \t\t} else {
                              \t\t\tsignerKey = SignatureValidator.recoverAddr(
                              \t\t\t\tkeccak256(abi.encode(address(this), block.chainid, currentNonce, calls)),
                              \t\t\t\tsignature,
                              \t\t\t\ttrue
                              \t\t\t);
                              \t\t\trequire(privileges[signerKey] != bytes32(0), 'INSUFFICIENT_PRIVILEGE');
                              \t\t}
                              \t\texecuteBatch(calls);
                              \t\t// The actual anti-bricking mechanism - do not allow a signerKey to drop their own privileges
                              \t\trequire(privileges[signerKey] != bytes32(0), 'PRIVILEGE_NOT_DOWNGRADED');
                              \t}
                              \t/**
                              \t * @notice  allows executing multiple bundles of calls (batch together multiple executes)
                              \t * @param   toExec  an array of execute function parameters
                              \t */
                              \tfunction executeMultiple(ExecuteArgs[] calldata toExec) external payable {
                              \t\tfor (uint256 i = 0; i != toExec.length; i++) execute(toExec[i].calls, toExec[i].signature);
                              \t}
                              \t/**
                              \t * @notice  Allows executing calls if the caller itself is authorized
                              \t * @dev     no need for nonce management here cause we're not dealing with sigs
                              \t * @param   calls  the transaction we're executing
                              \t */
                              \tfunction executeBySender(Transaction[] calldata calls) external payable {
                              \t\trequire(privileges[msg.sender] != bytes32(0), 'INSUFFICIENT_PRIVILEGE');
                              \t\texecuteBatch(calls);
                              \t\t// again, anti-bricking
                              \t\trequire(privileges[msg.sender] != bytes32(0), 'PRIVILEGE_NOT_DOWNGRADED');
                              \t}
                              \t/**
                              \t * @notice  allows the contract itself to execute a batch of calls
                              \t * self-calling is useful in cases like wanting to do multiple things in a tryCatchLimit
                              \t * @param   calls  the calls we're executing
                              \t */
                              \tfunction executeBySelf(Transaction[] calldata calls) external payable {
                              \t\trequire(msg.sender == address(this), 'ONLY_ACCOUNT_CAN_CALL');
                              \t\texecuteBatch(calls);
                              \t}
                              \t/**
                              \t * @notice  allows the contract itself to execute a single calls
                              \t * self-calling is useful when you want to workaround the executeBatch()
                              \t * protection of not being able to call address(0)
                              \t * @param   call  the call we're executing
                              \t */
                              \tfunction executeBySelfSingle(Transaction calldata call) external payable {
                              \t\trequire(msg.sender == address(this), 'ONLY_ACCOUNT_CAN_CALL');
                              \t\texecuteCall(call.to, call.value, call.data);
                              \t}
                              \t/**
                              \t * @notice  Execute a batch of transactions
                              \t * @param   calls  the transaction we're executing
                              \t */
                              \tfunction executeBatch(Transaction[] memory calls) internal {
                              \t\tuint256 len = calls.length;
                              \t\tfor (uint256 i = 0; i < len; i++) {
                              \t\t\tTransaction memory call = calls[i];
                              \t\t\tif (call.to != address(0)) executeCall(call.to, call.value, call.data);
                              \t\t}
                              \t}
                              \t/**
                              \t * @notice  Execute a signle transaction
                              \t * @dev     we shouldn't use address.call(), cause: https://github.com/ethereum/solidity/issues/2884
                              \t * @param   to  the address we're sending to
                              \t * @param   value  the amount we're sending
                              \t * @param   data  callData
                              \t */
                              \tfunction executeCall(address to, uint256 value, bytes memory data) internal {
                              \t\tassembly {
                              \t\t\tlet result := call(gas(), to, value, add(data, 0x20), mload(data), 0, 0)
                              \t\t\tif eq(result, 0) {
                              \t\t\t\tlet size := returndatasize()
                              \t\t\t\tlet ptr := mload(0x40)
                              \t\t\t\treturndatacopy(ptr, 0, size)
                              \t\t\t\trevert(ptr, size)
                              \t\t\t}
                              \t\t}
                              \t}
                              \t/**
                              \t * @notice  EIP-1271 implementation
                              \t * @dev     see https://eips.ethereum.org/EIPS/eip-1271
                              \t * @param   hash  the signed hash
                              \t * @param   signature  the signature for the signed hash
                              \t * @return  bytes4  is it a success or a failure
                              \t */
                              \tfunction isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {
                              \t\t(address recovered, bool usedUnprotected) = SignatureValidator.recoverAddrAllowUnprotected(hash, signature, false);
                              \t\tif (uint256(privileges[recovered]) > (usedUnprotected ? 1 : 0)) {
                              \t\t\t// bytes4(keccak256("isValidSignature(bytes32,bytes)")
                              \t\t\treturn 0x1626ba7e;
                              \t\t} else {
                              \t\t\treturn 0xffffffff;
                              \t\t}
                              \t}
                              \t/**
                              \t * @notice  EIP-1155 implementation
                              \t * we pretty much only need to signal that we support the interface for 165, but for 1155 we also need the fallback function
                              \t * @param   interfaceID  the interface we're signaling support for
                              \t * @return  bool  do we support the interface or not
                              \t */
                              \tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {
                              \t\tbool supported = interfaceID == 0x01ffc9a7 || // ERC-165 support (i.e. `bytes4(keccak256('supportsInterface(bytes4)'))`).
                              \t\t\tinterfaceID == 0x150b7a02 || // ERC721TokenReceiver
                              \t\t\tinterfaceID == 0x4e2312e0 || // ERC-1155 `ERC1155TokenReceiver` support (i.e. `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) ^ bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`).
                              \t\t\tinterfaceID == 0x0a417632; // used for checking whether the account is v2 or not
                              \t\tif (supported) return true;
                              \t\taddress payable fallbackHandler = payable(address(uint160(uint256(privileges[FALLBACK_HANDLER_SLOT]))));
                              \t\tif (fallbackHandler == address(0)) return false;
                              \t\treturn AmbireAccount(fallbackHandler).supportsInterface(interfaceID);
                              \t}
                              \t//
                              \t// EIP-4337 implementation
                              \t//
                              \t// return value in case of signature failure, with no time-range.
                              \t// equivalent to packSigTimeRange(true,0,0);
                              \tuint256 constant internal SIG_VALIDATION_FAILED = 1;
                              \t// equivalent to packSigTimeRange(false,0,0);
                              \tuint256 constant internal SIG_VALIDATION_SUCCESS = 0;
                              \t/**
                              \t * @notice  EIP-4337 implementation
                              \t * @dev     We have an edge case for enabling ERC-4337 in the first if statement.
                              \t * If the function call is to execute, we do not perform an userOp sig validation.
                              \t * We require a one time hash nonce commitment from the paymaster for the given
                              \t * req. We use this to give permissions to the entry point on the fly
                              \t * and enable ERC-4337
                              \t * @param   op  the PackedUserOperation we're executing
                              \t * @param   userOpHash  the hash we've committed to
                              \t * @param   missingAccountFunds  the funds the account needs to pay
                              \t * @return  uint256  0 for success, 1 for signature failure, and a uint256
                              \t * packed timestamp for a future valid signature:
                              \t * address aggregator, uint48 validUntil, uint48 validAfter
                              \t */
                              \tfunction validateUserOp(PackedUserOperation calldata op, bytes32 userOpHash, uint256 missingAccountFunds)
                              \texternal payable returns (uint256)
                              \t{
                              \t\t// enable running executeMultiple operation through the entryPoint if
                              \t\t// a paymaster sponsors it with a commitment one-time nonce.
                              \t\t// two use cases:
                              \t\t// 1) enable 4337 on a network by giving privileges to the entryPoint
                              \t\t// 2) key recovery. If the key is lost, we cannot sign the userOp,
                              \t\t// so we have to go to `execute` to trigger the recovery logic
                              \t\t// Why executeMultiple but not execute?
                              \t\t// executeMultiple allows us to combine recovery + fee payment calls.
                              \t\t// The fee payment call will be with a signature from the new key
                              \t\tif (op.callData.length >= 4 && bytes4(op.callData[0:4]) == this.executeMultiple.selector) {
                              \t\t\t// Require a paymaster, otherwise this mode can be used by anyone to get the user to spend their deposit
                              \t\t\t// @estimation-no-revert
                              \t\t\tif (op.signature.length != 0) return SIG_VALIDATION_FAILED;
                              \t\t\trequire(
                              \t\t\t\top.paymasterAndData.length >= UserOpHelper.PAYMASTER_DATA_OFFSET &&
                              \t\t\t\tbytes20(op.paymasterAndData[:UserOpHelper.PAYMASTER_ADDR_OFFSET]) != bytes20(0),
                              \t\t\t\t'validateUserOp: paymaster required in execute() mode'
                              \t\t\t);
                              \t\t\t// hashing in everything except sender (nonces are scoped by sender anyway), nonce, signature
                              \t\t\tuint256 targetNonce = uint256(keccak256(
                              \t\t\t\tabi.encode(op.initCode, op.callData, op.accountGasLimits, op.preVerificationGas, op.gasFees, op.paymasterAndData)
                              \t\t\t)) << 64;
                              \t\t\t// @estimation-no-revert
                              \t\t\tif (op.nonce != targetNonce) return SIG_VALIDATION_FAILED;
                              \t\t\treturn SIG_VALIDATION_SUCCESS;
                              \t\t}
                              \t\trequire(privileges[msg.sender] == ENTRY_POINT_MARKER, 'validateUserOp: not from entryPoint');
                              \t\t// @estimation
                              \t\t// paying should happen even if signature validation fails
                              \t\tif (missingAccountFunds > 0) {
                              \t\t\t// NOTE: MAY pay more than the minimum, to deposit for future transactions
                              \t\t\t(bool success,) = msg.sender.call{value : missingAccountFunds}('');
                              \t\t\t// ignore failure (its EntryPoint's job to verify, not account.)
                              \t\t\t(success);
                              \t\t}
                              \t\t// this is replay-safe because userOpHash is retrieved like this: keccak256(abi.encode(userOp.hash(), address(this), block.chainid))
                              \t\taddress signer = SignatureValidator.recoverAddr(userOpHash, op.signature, true);
                              \t\tif (privileges[signer] == bytes32(0)) return SIG_VALIDATION_FAILED;
                              \t\treturn SIG_VALIDATION_SUCCESS;
                              \t}
                              \tfunction validateExternalSig(Transaction[] memory calls, bytes calldata signature)
                              \tinternal returns(address signerKey, bool isValidSig, uint256 timestampValidAfter) {
                              \t\t(bytes memory sig, ) = SignatureValidator.splitSignature(signature);
                              \t\t// the address of the validator we're using for this validation
                              \t\taddress validatorAddr;
                              \t\t// all the data needed by the validator to execute the validation.
                              \t\t// In the case of DKIMRecoverySigValidator, this is AccInfo:
                              \t\t// abi.encode {string emailFrom; string emailTo; string domainName;
                              \t\t// bytes dkimPubKeyModulus; bytes dkimPubKeyExponent; address secondaryKey;
                              \t\t// bool acceptUnknownSelectors; uint32 waitUntilAcceptAdded;
                              \t\t// uint32 waitUntilAcceptRemoved; bool acceptEmptyDKIMSig;
                              \t\t// bool acceptEmptySecondSig;uint32 onlyOneSigTimelock;}
                              \t\t// The struct is declared in DKIMRecoverySigValidator
                              \t\tbytes memory validatorData;
                              \t\t// the signature data needed by the external validator.
                              \t\t// In the case of DKIMRecoverySigValidator, this is abi.encode(
                              \t\t// SignatureMeta memory sigMeta, bytes memory dkimSig, bytes memory secondSig
                              \t\t// ).
                              \t\tbytes memory innerSig;
                              \t\t// the signerKey in this case is an arbitrary value that does
                              \t\t// not have any specific purpose other than representing
                              \t\t// the privileges key
                              \t\t(signerKey, validatorAddr, validatorData, innerSig) = abi.decode(sig, (address, address, bytes, bytes));
                              \t\trequire(
                              \t\t\tprivileges[signerKey] == keccak256(abi.encode(validatorAddr, validatorData)),
                              \t\t\t'EXTERNAL_VALIDATION_NOT_SET'
                              \t\t);
                              \t\t// The sig validator itself should throw when a signature isn't validated successfully
                              \t\t// the return value just indicates whether we want to execute the current calls
                              \t\t(isValidSig, timestampValidAfter) = ExternalSigValidator(validatorAddr).validateSig(validatorData, innerSig, calls);
                              \t}
                              }
                              // SPDX-License-Identifier: agpl-3.0
                              pragma solidity 0.8.19;
                              import './deployless/IAmbireAccount.sol';
                              import './libs/Transaction.sol';
                              /**
                               * @notice  A contract used for deploying AmbireAccount.sol
                               * @dev     We use create2 to get the AmbireAccount address. It's deterministic:
                               * if the same data is passed to it, the same address will pop out.
                               */
                              contract AmbireFactory {
                              \tevent LogDeployed(address addr, uint256 salt);
                              \taddress public immutable allowedToDrain;
                              \tconstructor(address allowed) {
                              \t\tallowedToDrain = allowed;
                              \t}
                              \t/**
                              \t * @notice  Allows anyone to deploy any contracft with a specific code/salt
                              \t * @dev     This is safe because it's CREATE2 deployment
                              \t * @param   code  the code to be deployed
                              \t * @param   salt  the salt to shuffle the computed address
                              \t * @return  address  the deployed address
                              \t */
                              \tfunction deploy(bytes calldata code, uint256 salt) external returns(address) {
                              \t\treturn deploySafe(code, salt);
                              \t}
                              \t
                              \t/**
                              \t * @notice  Call this when you want to deploy the contract and execute calls
                              \t * @dev     When the relayer needs to act upon an /identity/:addr/submit call, it'll either call execute on the AmbireAccount directly
                              \t * if it's already deployed, or call `deployAndExecute` if the account is still counterfactual
                              \t * we can't have deployAndExecuteBySender, because the sender will be the factory
                              \t * @param   code  the code to be deployed
                              \t * @param   salt  the salt to shuffle the computed address
                              \t * @param   txns  the txns the are going to be executed
                              \t * @param   signature  the signature for the txns
                              \t * @return  address  the deployed address
                              \t */
                              \tfunction deployAndExecute(
                              \t\tbytes calldata code,
                              \t\tuint256 salt,
                              \t\tTransaction[] calldata txns,
                              \t\tbytes calldata signature
                              \t) external returns (address){
                              \t\taddress payable addr = payable(deploySafe(code, salt));
                              \t\tIAmbireAccount(addr).execute(txns, signature);
                              \t\treturn addr;
                              \t}
                              \t
                              \t/**
                              \t * @notice  Call this when you want to deploy the contract and call executeMultiple
                              \t * @dev     when the relayer needs to act upon an /identity/:addr/submit call, 
                              \t * it'll either call execute on the AmbireAccount directly. If it's already
                              \t * deployed, or call `deployAndExecuteMultiple` if the account is still
                              \t * counterfactual but there are multiple accountOps to send
                              \t * @param   code  the code to be deployed
                              \t * @param   salt  the salt to shuffle the computed address
                              \t * @param   toExec  [txns, signature] execute parameters
                              \t * @return  address  the deployed address
                              \t */
                              \tfunction deployAndExecuteMultiple(
                              \t\tbytes calldata code,
                              \t\tuint256 salt,
                              \t\tIAmbireAccount.ExecuteArgs[] calldata toExec
                              \t) external returns (address){
                              \t\taddress payable addr = payable(deploySafe(code, salt));
                              \t\tIAmbireAccount(addr).executeMultiple(toExec);
                              \t\treturn addr;
                              \t}
                              \t/**
                              \t * @notice  This method can be used to withdraw stuck tokens or airdrops
                              \t * @dev     Only allowedToDrain can do the call
                              \t * @param   to  receiver
                              \t * @param   value  how much to be sent
                              \t * @param   data  if a token has airdropped, code to send it
                              \t * @param   gas  maximum gas willing to spend
                              \t */
                              \tfunction call(address to, uint256 value, bytes calldata data, uint256 gas) external {
                              \t\trequire(msg.sender == allowedToDrain, 'ONLY_AUTHORIZED');
                              \t\t(bool success, bytes memory err) = to.call{ gas: gas, value: value }(data);
                              \t\trequire(success, string(err));
                              \t}
                              \t
                              \t/**
                              \t * @dev     This is done to mitigate possible frontruns where, for example,
                              \t * where deploying the same code/salt via deploy() would make a pending
                              \t * deployAndExecute fail. The way we mitigate that is by checking if the
                              \t * contract is already deployed and if so, we continue execution
                              \t * @param   code  the code to be deployed
                              \t * @param   salt  the salt to shuffle the computed address
                              \t * @return  address  the deployed address
                              \t */
                              \tfunction deploySafe(bytes memory code, uint256 salt) internal returns (address) {
                              \t\taddress expectedAddr = address(
                              \t\t\tuint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, keccak256(code)))))
                              \t\t);
                              \t\tuint256 size;
                              \t\tassembly {
                              \t\t\tsize := extcodesize(expectedAddr)
                              \t\t}
                              \t\t// If there is code at that address, we can assume it's the one we were about to deploy,
                              \t\t// because of how CREATE2 and keccak256 works
                              \t\tif (size == 0) {
                              \t\t\taddress addr;
                              \t\t\tassembly {
                              \t\t\t\taddr := create2(0, add(code, 0x20), mload(code), salt)
                              \t\t\t}
                              \t\t\trequire(addr != address(0), 'FAILED_DEPLOYING');
                              \t\t\trequire(addr == expectedAddr, 'FAILED_MATCH');
                              \t\t\temit LogDeployed(addr, salt);
                              \t\t}
                              \t\treturn expectedAddr;
                              \t}
                              }
                              // SPDX-License-Identifier: agpl-3.0
                              pragma solidity 0.8.19;
                              import './deployless/IAmbireAccount.sol';
                              import './libs/erc4337/IPaymaster.sol';
                              import './libs/SignatureValidator.sol';
                              import './libs/erc4337/UserOpHelper.sol';
                              contract AmbirePaymaster is IPaymaster {
                              \taddress immutable public relayer;
                              \tconstructor(address _relayer) {
                              \t\trelayer = _relayer;
                              \t}
                              \t/**
                              \t * @notice  This method can be used to withdraw stuck tokens or airdrops
                              \t *
                              \t * @param   to  The address we're calling
                              \t * @param   value  The value in the call
                              \t * @param\tdata\tthe call data
                              \t * @param\tgas\tthe call gas
                              \t */
                              \tfunction call(address to, uint256 value, bytes calldata data, uint256 gas) external payable {
                              \t\trequire(msg.sender == relayer, 'call: not relayer');
                              \t\t(bool success, bytes memory err) = to.call{ gas: gas, value: value }(data);
                              \t\trequire(success, string(err));
                              \t}
                              \t/**
                              \t * @notice  Validate user operations the paymaster has signed
                              \t * We do not need to send funds to the EntryPoint because we rely on pre-existing deposit.
                              \t * Requests are chain specific to prevent signature reuse.
                              \t * @dev     We have two use cases for the paymaster:
                              \t * - normal erc-4337. Everything is per ERC-4337 standard, the nonce is sequential.
                              \t * - an executeMultiple call. If the calldata is executeMultiple, we've hardcoded
                              \t * a 0 nonce. That's what's called a one-time hash nonce and its key is actually
                              \t * the commitment. Check EntryPoint -> NonceManager for more information.
                              \t *
                              \t * @param   userOp  the UserOperation we're executing
                              \t * @return  context  context is returned in the postOp and called by the
                              \t * EntryPoint. But we're not using postOp is context is always emtpy
                              \t * @return  validationData  This consists of:
                              \t * - an aggregator address: address(uint160(validationData)). This is used
                              \t * when you want an outer contract to determine whether the signature is valid.
                              \t * In our case, this is always 0 (address 0) for valid signatures and
                              \t * 1 (address 1) for invalid. This is what the entry point expects and
                              \t * in those two cases, an outer contract is obviously not called.
                              \t * - a uint48 validUntil: uint48(validationData >> 160)
                              \t * A Paymaster signature can be signed at time "x" but delayed intentionally
                              \t * until time "y" when a fee payment's price has dropped significantly or
                              \t * some other issue. validUntil sets a time validity for the signature
                                   * - a uint48 validAfter: uint48(validationData >> (48 + 160))
                              \t * If the signature should be valid only after a period of time,
                              \t * we tweak the validAfter property.
                              \t * For more information, check EntryPoint -> _getValidationData()
                              \t */
                              \tfunction validatePaymasterUserOp(PackedUserOperation calldata userOp, bytes32, uint256)
                              \t\texternal
                              \t\tview
                              \t\treturns (bytes memory context, uint256 validationData)
                              \t{
                              \t\t(uint48 validUntil, uint48 validAfter, bytes memory signature) = abi.decode(
                              \t\t\tuserOp.paymasterAndData[UserOpHelper.PAYMASTER_DATA_OFFSET:],
                              \t\t\t(uint48, uint48, bytes)
                              \t\t);
                              \t\tbytes memory callData = userOp.callData;
                              \t\tbytes32 hash = keccak256(abi.encode(
                              \t\t\tblock.chainid,
                              \t\t\taddress(this),
                              \t\t\t// entry point
                              \t\t\tmsg.sender,
                              \t\t\tvalidUntil,
                              \t\t\tvalidAfter,
                              \t\t\t// everything except paymasterAndData and signature
                              \t\t\tuserOp.sender,
                              \t\t\t// for the nonce we have an exception case: one-time nonces depend on paymasterAndData, which is generated by the relayer
                              \t\t\t// we can't have this as part of the sig cuz we create a cyclical dep
                              \t\t\t// the nonce can only be used once, so one cannot replay the gas payment
                              \t\t\tcallData.length >= 4 && bytes4(userOp.callData[0:4]) == IAmbireAccount.executeMultiple.selector ? 0 : userOp.nonce,
                              \t\t\tuserOp.initCode,
                              \t\t\tcallData,
                              \t\t\tuserOp.accountGasLimits,
                              \t\t\tuserOp.preVerificationGas,
                              \t\t\tuserOp.gasFees
                              \t\t));
                              \t\t(address recovered, ) = SignatureValidator.recoverAddrAllowUnprotected(hash, signature, true);
                              \t\tbool isValidSig = recovered == relayer;
                              \t\t// see _packValidationData: https://github.com/eth-infinitism/account-abstraction/blob/f2b09e60a92d5b3177c68d9f382912ccac19e8db/contracts/core/Helpers.sol#L73-L80
                              \t\treturn ("", uint160(isValidSig ? 0 : 1) | (uint256(validUntil) << 160) | (uint256(validAfter) << 208));
                              \t}
                              \t/**
                              \t * @notice  No-op, won't be used because we don't return a context
                              \t * @param   mode  .
                              \t * @param   context  .
                              \t * @param   actualGasCost  .
                              \t */
                              \tfunction postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external {
                              \t\t// No-op, won't be used because we don't return a context
                              \t}
                              }
                              // SPDX-License-Identifier: agpl-3.0
                              pragma solidity 0.8.19;
                              import './libs/Transaction.sol';
                              /**
                               * @title   ExternalSigValidator
                               * @notice  A way to add custom recovery to AmbireAccount.
                               * address accountAddr is the Ambire account address
                               * bytes calldata data is all the data needed by the ExternalSigValidator.
                               * It could be anything and it's validator specific.
                               * bytes calldata sig is the signature we're validating. Notice its not
                               * bytes32 so there could be cases where its not only the signature. It's
                               * validator specific
                               * uint256 nonce - the Ambire account nonce
                               * Transaction[] calldata calls - the txns that are going to be executed
                               * if the validation is successful
                               * @dev     Not all passed properties necessarily need to be used.
                               */
                              abstract contract ExternalSigValidator {
                              \tfunction validateSig(
                              \t\tbytes calldata data,
                              \t\tbytes calldata sig,
                              \t\tTransaction[] calldata calls
                              \t) external virtual returns (bool isValidSignature, uint256 timestampValidAfter);
                              }// SPDX-License-Identifier: agpl-3.0
                              pragma solidity ^0.8.7;
                              import '../libs/Transaction.sol';
                              interface IAmbireAccount {
                              \tfunction privileges(address addr) external returns (bytes32);
                              \tfunction nonce() external returns (uint);
                              \tstruct RecoveryInfo {
                              \t\taddress[] keys;
                              \t\tuint timelock;
                              \t}
                              \tstruct ExecuteArgs {
                              \t\tTransaction[] calls;
                              \t\tbytes signature;
                              \t}
                              \tfunction setAddrPrivilege(address addr, bytes32 priv) external payable;
                              \tfunction tryCatch(address to, uint value, bytes calldata data) external payable;
                              \tfunction tryCatchLimit(address to, uint value, bytes calldata data, uint gasLimit) external payable;
                              \tfunction execute(Transaction[] calldata txns, bytes calldata signature) external payable;
                              \tfunction executeBySender(Transaction[] calldata txns) external payable;
                              \tfunction executeBySelf(Transaction[] calldata txns) external payable;
                              \tfunction executeMultiple(ExecuteArgs[] calldata toExec) external payable;
                              \t// EIP 1271 implementation
                              \t// see https://eips.ethereum.org/EIPS/eip-1271
                              \tfunction isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4);
                              \tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);
                              }
                              // SPDX-License-Identifier: agpl-3.0
                              pragma solidity 0.8.19;
                              library Bytes {
                              \tfunction trimToSize(bytes memory b, uint256 newLen) internal pure {
                              \t\trequire(b.length > newLen, 'BytesLib: only shrinking');
                              \t\tassembly {
                              \t\t\tmstore(b, newLen)
                              \t\t}
                              \t}
                              \t/***********************************|
                              \t|        Read Bytes Functions       |
                              \t|__________________________________*/
                              \t/**
                              \t * @dev Reads a bytes32 value from a position in a byte array.
                              \t * @param b Byte array containing a bytes32 value.
                              \t * @param index Index in byte array of bytes32 value.
                              \t * @return result bytes32 value from byte array.
                              \t */
                              \tfunction readBytes32(bytes memory b, uint256 index) internal pure returns (bytes32 result) {
                              \t\t// Arrays are prefixed by a 256 bit length parameter
                              \t\tindex += 32;
                              \t\trequire(b.length >= index, 'BytesLib: length');
                              \t\t// Read the bytes32 from array memory
                              \t\tassembly {
                              \t\t\tresult := mload(add(b, index))
                              \t\t}
                              \t\treturn result;
                              \t}
                              }
                              // SPDX-License-Identifier: agpl-3.0
                              pragma solidity 0.8.19;
                              import './Bytes.sol';
                              interface IERC1271Wallet {
                              \tfunction isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4 magicValue);
                              }
                              library SignatureValidator {
                              \tusing Bytes for bytes;
                              \tenum SignatureMode {
                              \t\t// the first mode Unprotected is used in combination with EIP-1271 signature verification to do
                              \t\t// EIP-712 verifications, as well as "Ethereum signed message:" message verifications
                              \t\t// The caveat with this is that we need to ensure that the signer key used for it isn't reused, or the message body
                              \t\t// itself contains context about the wallet (such as it's address)
                              \t\t// We do this, rather than applying the prefix on-chain, because if we do you won't be able to see the message
                              \t\t// when signing on a hardware wallet (you'll only see the hash) - since `isValidSignature` can only receive the hash -
                              \t\t// if the prefix is applied on-chain you can never match it - it's hash(prefix+hash(msg)) vs hash(prefix+msg)
                              \t\t// As for transactions (`execute()`), those can be signed with any of the modes
                              \t\t// Otherwise, if it's reused, we MUST use `Standard` mode which always wraps the final digest hash, but unfortnately this means
                              \t\t// you can't preview the full message when signing on a HW wallet
                              \t\tUnprotected,
                              \t\tStandard,
                              \t\tSmartWallet,
                              \t\tSpoof,
                              \t\tSchnorr,
                              \t\tMultisig,
                              \t\t// WARNING: Signature modes should not be more than 26 as the "v"
                              \t\t// value for standard ecrecover is 27/28
                              \t\t// WARNING: must always be last
                              \t\tLastUnused
                              \t}
                              \t// bytes4(keccak256("isValidSignature(bytes32,bytes)"))
                              \tbytes4 internal constant ERC1271_MAGICVALUE_BYTES32 = 0x1626ba7e;
                              \t// secp256k1 group order
                              \tuint256 internal constant Q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
                              \tfunction splitSignature(bytes memory sig) internal pure returns (bytes memory, uint8) {
                              \t\tuint8 modeRaw;
                              \t\tunchecked {
                              \t\t\tmodeRaw = uint8(sig[sig.length - 1]);
                              \t\t}
                              \t\tsig.trimToSize(sig.length - 1);
                              \t\treturn (sig, modeRaw);
                              \t}
                              \tfunction recoverAddr(bytes32 hash, bytes memory sig, bool allowSpoofing) internal view returns (address) {
                              \t\t(address recovered, bool usedUnprotected) = recoverAddrAllowUnprotected(hash, sig, allowSpoofing);
                              \t\trequire(!usedUnprotected, 'SV_USED_UNBOUND');
                              \t\treturn recovered;
                              \t}
                              \tfunction recoverAddrAllowUnprotected(bytes32 hash, bytes memory sig, bool allowSpoofing) internal view returns (address, bool) {
                              \t\trequire(sig.length != 0, 'SV_SIGLEN');
                              \t\tuint8 modeRaw;
                              \t\tunchecked {
                              \t\t\tmodeRaw = uint8(sig[sig.length - 1]);
                              \t\t}
                              \t\t// Ensure we're in bounds for mode; Solidity does this as well but it will just silently blow up rather than showing a decent error
                              \t\tif (modeRaw >= uint8(SignatureMode.LastUnused)) {
                              \t\t\tif (sig.length == 65) modeRaw = uint8(SignatureMode.Unprotected);
                              \t\t\telse revert('SV_SIGMODE');
                              \t\t}
                              \t\tSignatureMode mode = SignatureMode(modeRaw);
                              \t\t// the address of the key we are gonna be returning
                              \t\taddress signerKey;
                              \t\t// wrap in the EIP712 wrapping if it's not unbound
                              \t\t// multisig gets an exception because each inner sig will have to apply this logic
                              \t\t// @TODO should spoofing be removed from this?
                              \t\tbool isUnprotected = mode == SignatureMode.Unprotected || mode == SignatureMode.Multisig;
                              \t\tif (!isUnprotected) {
                              \t\t\tbytes32 DOMAIN_SEPARATOR = keccak256(abi.encode(
                              \t\t\t\tkeccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)'),
                              \t\t\t\tkeccak256(bytes('Ambire')),
                              \t\t\t\tkeccak256(bytes('1')),
                              \t\t\t\tblock.chainid,
                              \t\t\t\taddress(this),
                              \t\t\t\tbytes32(0)
                              \t\t\t));
                              \t\t\thash = keccak256(abi.encodePacked(
                              \t\t\t\t'\\x19\\x01',
                              \t\t\t\tDOMAIN_SEPARATOR,
                              \t\t\t\tkeccak256(abi.encode(
                              \t\t\t\t\tkeccak256(bytes('AmbireOperation(address account,bytes32 hash)')),
                              \t\t\t\t\taddress(this),
                              \t\t\t\t\thash
                              \t\t\t\t))
                              \t\t\t));
                              \t\t}
                              \t\t// {r}{s}{v}{mode}
                              \t\tif (mode == SignatureMode.Unprotected || mode == SignatureMode.Standard) {
                              \t\t\trequire(sig.length == 65 || sig.length == 66, 'SV_LEN');
                              \t\t\tbytes32 r = sig.readBytes32(0);
                              \t\t\tbytes32 s = sig.readBytes32(32);
                              \t\t\tuint8 v = uint8(sig[64]);
                              \t\t\tsignerKey = ecrecover(hash, v, r, s);
                              \t\t// {sig}{verifier}{mode}
                              \t\t} else if (mode == SignatureMode.Schnorr) {
                              \t\t\t// Based on https://hackmd.io/@nZ-twauPRISEa6G9zg3XRw/SyjJzSLt9
                              \t\t\t// You can use this library to produce signatures: https://github.com/borislav-itskov/schnorrkel.js
                              \t\t\t// px := public key x-coord
                              \t\t\t// e := schnorr signature challenge
                              \t\t\t// s := schnorr signature
                              \t\t\t// parity := public key y-coord parity (27 or 28)
                              \t\t\t// last uint8 is for the Ambire sig mode - it's ignored
                              \t\t\tsig.trimToSize(sig.length - 1);
                              \t\t\t(bytes32 px, bytes32 e, bytes32 s, uint8 parity) = abi.decode(sig, (bytes32, bytes32, bytes32, uint8));
                              \t\t\t// ecrecover = (m, v, r, s);
                              \t\t\tbytes32 sp = bytes32(Q - mulmod(uint256(s), uint256(px), Q));
                              \t\t\tbytes32 ep = bytes32(Q - mulmod(uint256(e), uint256(px), Q));
                              \t\t\trequire(sp != bytes32(Q));
                              \t\t\t// the ecrecover precompile implementation checks that the `r` and `s`
                              \t\t\t// inputs are non-zero (in this case, `px` and `ep`), thus we don't need to
                              \t\t\t// check if they're zero.
                              \t\t\taddress R = ecrecover(sp, parity, px, ep);
                              \t\t\trequire(R != address(0), 'SV_ZERO_SIG');
                              \t\t\trequire(e == keccak256(abi.encodePacked(R, uint8(parity), px, hash)), 'SV_SCHNORR_FAILED');
                              \t\t\tsignerKey = address(uint160(uint256(keccak256(abi.encodePacked('SCHNORR', px)))));
                              \t\t} else if (mode == SignatureMode.Multisig) {
                              \t\t\tsig.trimToSize(sig.length - 1);
                              \t\t\tbytes[] memory signatures = abi.decode(sig, (bytes[]));
                              \t\t\t// since we're in a multisig, we care if any of the inner sigs are unbound
                              \t\t\tisUnprotected = false;
                              \t\t\tfor (uint256 i = 0; i != signatures.length; i++) {
                              \t\t\t\t(address inner, bool isInnerUnprotected) = recoverAddrAllowUnprotected(hash, signatures[i], false);
                              \t\t\t\tif (isInnerUnprotected) isUnprotected = true;
                              \t\t\t\tsignerKey = address(
                              \t\t\t\t\tuint160(uint256(keccak256(abi.encodePacked(signerKey, inner))))
                              \t\t\t\t);
                              \t\t\t}
                              \t\t} else if (mode == SignatureMode.SmartWallet) {
                              \t\t\t// 32 bytes for the addr, 1 byte for the type = 33
                              \t\t\trequire(sig.length > 33, 'SV_LEN_WALLET');
                              \t\t\tuint256 newLen;
                              \t\t\tunchecked {
                              \t\t\t\tnewLen = sig.length - 33;
                              \t\t\t}
                              \t\t\tIERC1271Wallet wallet = IERC1271Wallet(address(uint160(uint256(sig.readBytes32(newLen)))));
                              \t\t\tsig.trimToSize(newLen);
                              \t\t\trequire(ERC1271_MAGICVALUE_BYTES32 == wallet.isValidSignature(hash, sig), 'SV_WALLET_INVALID');
                              \t\t\tsignerKey = address(wallet);
                              \t\t// {address}{mode}; the spoof mode is used when simulating calls
                              \t\t} else if (mode == SignatureMode.Spoof && allowSpoofing) {
                              \t\t\t// This is safe cause it's specifically intended for spoofing sigs in simulation conditions, where tx.origin can be controlled
                              \t\t\t// We did not choose 0x00..00 because in future network upgrades tx.origin may be nerfed or there may be edge cases in which
                              \t\t\t// it is zero, such as native account abstraction
                              \t\t\t// slither-disable-next-line tx-origin
                              \t\t\trequire(tx.origin == address(1) || tx.origin == address(6969), 'SV_SPOOF_ORIGIN');
                              \t\t\trequire(sig.length == 33, 'SV_SPOOF_LEN');
                              \t\t\tsig.trimToSize(32);
                              \t\t\t// To simulate the gas usage; check is just to silence unused warning
                              \t\t\trequire(ecrecover(0, 0, 0, 0) != address(6969));
                              \t\t\tsignerKey = abi.decode(sig, (address));
                              \t\t} else {
                              \t\t\trevert('SV_TYPE');
                              \t\t}
                              \t\trequire(signerKey != address(0), 'SV_ZERO_SIG');
                              \t\treturn (signerKey, isUnprotected);
                              \t}
                              }
                              // SPDX-License-Identifier: agpl-3.0
                              pragma solidity 0.8.19;
                              // Transaction structure
                              // we handle replay protection separately by requiring (address(this), chainID, nonce) as part of the sig
                              // @dev a better name for this would be `Call`, but we are keeping `Transaction` for backwards compatibility
                              struct Transaction {
                                  address to;
                                  uint256 value;
                                  bytes data;
                              }
                              // SPDX-License-Identifier: GPL-3.0
                              pragma solidity ^0.8.12;
                              import "./PackedUserOperation.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(PackedUserOperation 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: agpl-3.0
                              pragma solidity 0.8.19;
                              /**
                               * 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 accountGasLimits      - Packed gas limits for validateUserOp and gas limit passed to the callData method call.
                               * @param preVerificationGas    - Gas not calculated by the handleOps method, but added to the gas paid.
                               *                                Covers batch overhead.
                               * @param gasFees               - packed gas fields maxPriorityFeePerGas and maxFeePerGas - Same as EIP-1559 gas parameters.
                               * @param paymasterAndData      - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra 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 PackedUserOperation {
                                address sender;
                                uint256 nonce;
                                bytes initCode;
                                bytes callData;
                                // callGasLimit + verificationGasLimit
                                bytes32 accountGasLimits;
                                uint256 preVerificationGas;
                                // maxFeePerGas + maxPriorityFeePerGas
                                bytes32 gasFees;
                                bytes paymasterAndData;
                                bytes signature;
                              }
                              // SPDX-License-Identifier: agpl-3.0
                              pragma solidity 0.8.19;
                              library UserOpHelper {
                              \tuint256 public constant PAYMASTER_ADDR_OFFSET = 20;
                                // 52 = 20 address + 16 paymasterVerificationGasLimit + 16 paymasterPostOpGasLimit
                              \tuint256 public constant PAYMASTER_DATA_OFFSET = 52;
                              }
                              

                              File 5 of 6: FiatTokenV2_2
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              import { EIP712Domain } from "./EIP712Domain.sol"; // solhint-disable-line no-unused-import
                              import { Blacklistable } from "../v1/Blacklistable.sol"; // solhint-disable-line no-unused-import
                              import { FiatTokenV1 } from "../v1/FiatTokenV1.sol"; // solhint-disable-line no-unused-import
                              import { FiatTokenV2 } from "./FiatTokenV2.sol"; // solhint-disable-line no-unused-import
                              import { FiatTokenV2_1 } from "./FiatTokenV2_1.sol";
                              import { EIP712 } from "../util/EIP712.sol";
                              // solhint-disable func-name-mixedcase
                              /**
                               * @title FiatToken V2.2
                               * @notice ERC20 Token backed by fiat reserves, version 2.2
                               */
                              contract FiatTokenV2_2 is FiatTokenV2_1 {
                                  /**
                                   * @notice Initialize v2.2
                                   * @param accountsToBlacklist   A list of accounts to migrate from the old blacklist
                                   * @param newSymbol             New token symbol
                                   * data structure to the new blacklist data structure.
                                   */
                                  function initializeV2_2(
                                      address[] calldata accountsToBlacklist,
                                      string calldata newSymbol
                                  ) external {
                                      // solhint-disable-next-line reason-string
                                      require(_initializedVersion == 2);
                                      // Update fiat token symbol
                                      symbol = newSymbol;
                                      // Add previously blacklisted accounts to the new blacklist data structure
                                      // and remove them from the old blacklist data structure.
                                      for (uint256 i = 0; i < accountsToBlacklist.length; i++) {
                                          require(
                                              _deprecatedBlacklisted[accountsToBlacklist[i]],
                                              "FiatTokenV2_2: Blacklisting previously unblacklisted account!"
                                          );
                                          _blacklist(accountsToBlacklist[i]);
                                          delete _deprecatedBlacklisted[accountsToBlacklist[i]];
                                      }
                                      _blacklist(address(this));
                                      delete _deprecatedBlacklisted[address(this)];
                                      _initializedVersion = 3;
                                  }
                                  /**
                                   * @dev Internal function to get the current chain id.
                                   * @return The current chain id.
                                   */
                                  function _chainId() internal virtual view returns (uint256) {
                                      uint256 chainId;
                                      assembly {
                                          chainId := chainid()
                                      }
                                      return chainId;
                                  }
                                  /**
                                   * @inheritdoc EIP712Domain
                                   */
                                  function _domainSeparator() internal override view returns (bytes32) {
                                      return EIP712.makeDomainSeparator(name, "2", _chainId());
                                  }
                                  /**
                                   * @notice Update allowance with a signed permit
                                   * @dev EOA wallet signatures should be packed in the order of r, s, v.
                                   * @param owner       Token owner's address (Authorizer)
                                   * @param spender     Spender's address
                                   * @param value       Amount of allowance
                                   * @param deadline    The time at which the signature expires (unix time), or max uint256 value to signal no expiration
                                   * @param signature   Signature bytes signed by an EOA wallet or a contract wallet
                                   */
                                  function permit(
                                      address owner,
                                      address spender,
                                      uint256 value,
                                      uint256 deadline,
                                      bytes memory signature
                                  ) external whenNotPaused {
                                      _permit(owner, spender, value, deadline, signature);
                                  }
                                  /**
                                   * @notice Execute a transfer with a signed authorization
                                   * @dev EOA wallet signatures should be packed in the order of r, s, v.
                                   * @param from          Payer's address (Authorizer)
                                   * @param to            Payee's address
                                   * @param value         Amount to be transferred
                                   * @param validAfter    The time after which this is valid (unix time)
                                   * @param validBefore   The time before which this is valid (unix time)
                                   * @param nonce         Unique nonce
                                   * @param signature     Signature bytes signed by an EOA wallet or a contract wallet
                                   */
                                  function transferWithAuthorization(
                                      address from,
                                      address to,
                                      uint256 value,
                                      uint256 validAfter,
                                      uint256 validBefore,
                                      bytes32 nonce,
                                      bytes memory signature
                                  ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                                      _transferWithAuthorization(
                                          from,
                                          to,
                                          value,
                                          validAfter,
                                          validBefore,
                                          nonce,
                                          signature
                                      );
                                  }
                                  /**
                                   * @notice Receive a transfer with a signed authorization from the payer
                                   * @dev This has an additional check to ensure that the payee's address
                                   * matches the caller of this function to prevent front-running attacks.
                                   * EOA wallet signatures should be packed in the order of r, s, v.
                                   * @param from          Payer's address (Authorizer)
                                   * @param to            Payee's address
                                   * @param value         Amount to be transferred
                                   * @param validAfter    The time after which this is valid (unix time)
                                   * @param validBefore   The time before which this is valid (unix time)
                                   * @param nonce         Unique nonce
                                   * @param signature     Signature bytes signed by an EOA wallet or a contract wallet
                                   */
                                  function receiveWithAuthorization(
                                      address from,
                                      address to,
                                      uint256 value,
                                      uint256 validAfter,
                                      uint256 validBefore,
                                      bytes32 nonce,
                                      bytes memory signature
                                  ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                                      _receiveWithAuthorization(
                                          from,
                                          to,
                                          value,
                                          validAfter,
                                          validBefore,
                                          nonce,
                                          signature
                                      );
                                  }
                                  /**
                                   * @notice Attempt to cancel an authorization
                                   * @dev Works only if the authorization is not yet used.
                                   * EOA wallet signatures should be packed in the order of r, s, v.
                                   * @param authorizer    Authorizer's address
                                   * @param nonce         Nonce of the authorization
                                   * @param signature     Signature bytes signed by an EOA wallet or a contract wallet
                                   */
                                  function cancelAuthorization(
                                      address authorizer,
                                      bytes32 nonce,
                                      bytes memory signature
                                  ) external whenNotPaused {
                                      _cancelAuthorization(authorizer, nonce, signature);
                                  }
                                  /**
                                   * @dev Helper method that sets the blacklist state of an account on balanceAndBlacklistStates.
                                   * If _shouldBlacklist is true, we apply a (1 << 255) bitmask with an OR operation on the
                                   * account's balanceAndBlacklistState. This flips the high bit for the account to 1,
                                   * indicating that the account is blacklisted.
                                   *
                                   * If _shouldBlacklist if false, we reset the account's balanceAndBlacklistStates to their
                                   * balances. This clears the high bit for the account, indicating that the account is unblacklisted.
                                   * @param _account         The address of the account.
                                   * @param _shouldBlacklist True if the account should be blacklisted, false if the account should be unblacklisted.
                                   */
                                  function _setBlacklistState(address _account, bool _shouldBlacklist)
                                      internal
                                      override
                                  {
                                      balanceAndBlacklistStates[_account] = _shouldBlacklist
                                          ? balanceAndBlacklistStates[_account] | (1 << 255)
                                          : _balanceOf(_account);
                                  }
                                  /**
                                   * @dev Helper method that sets the balance of an account on balanceAndBlacklistStates.
                                   * Since balances are stored in the last 255 bits of the balanceAndBlacklistStates value,
                                   * we need to ensure that the updated balance does not exceed (2^255 - 1).
                                   * Since blacklisted accounts' balances cannot be updated, the method will also
                                   * revert if the account is blacklisted
                                   * @param _account The address of the account.
                                   * @param _balance The new fiat token balance of the account (max: (2^255 - 1)).
                                   */
                                  function _setBalance(address _account, uint256 _balance) internal override {
                                      require(
                                          _balance <= ((1 << 255) - 1),
                                          "FiatTokenV2_2: Balance exceeds (2^255 - 1)"
                                      );
                                      require(
                                          !_isBlacklisted(_account),
                                          "FiatTokenV2_2: Account is blacklisted"
                                      );
                                      balanceAndBlacklistStates[_account] = _balance;
                                  }
                                  /**
                                   * @inheritdoc Blacklistable
                                   */
                                  function _isBlacklisted(address _account)
                                      internal
                                      override
                                      view
                                      returns (bool)
                                  {
                                      return balanceAndBlacklistStates[_account] >> 255 == 1;
                                  }
                                  /**
                                   * @dev Helper method to obtain the balance of an account. Since balances
                                   * are stored in the last 255 bits of the balanceAndBlacklistStates value,
                                   * we apply a ((1 << 255) - 1) bit bitmask with an AND operation on the
                                   * balanceAndBlacklistState to obtain the balance.
                                   * @param _account  The address of the account.
                                   * @return          The fiat token balance of the account.
                                   */
                                  function _balanceOf(address _account)
                                      internal
                                      override
                                      view
                                      returns (uint256)
                                  {
                                      return balanceAndBlacklistStates[_account] & ((1 << 255) - 1);
                                  }
                                  /**
                                   * @inheritdoc FiatTokenV1
                                   */
                                  function approve(address spender, uint256 value)
                                      external
                                      override
                                      whenNotPaused
                                      returns (bool)
                                  {
                                      _approve(msg.sender, spender, value);
                                      return true;
                                  }
                                  /**
                                   * @inheritdoc FiatTokenV2
                                   */
                                  function permit(
                                      address owner,
                                      address spender,
                                      uint256 value,
                                      uint256 deadline,
                                      uint8 v,
                                      bytes32 r,
                                      bytes32 s
                                  ) external override whenNotPaused {
                                      _permit(owner, spender, value, deadline, v, r, s);
                                  }
                                  /**
                                   * @inheritdoc FiatTokenV2
                                   */
                                  function increaseAllowance(address spender, uint256 increment)
                                      external
                                      override
                                      whenNotPaused
                                      returns (bool)
                                  {
                                      _increaseAllowance(msg.sender, spender, increment);
                                      return true;
                                  }
                                  /**
                                   * @inheritdoc FiatTokenV2
                                   */
                                  function decreaseAllowance(address spender, uint256 decrement)
                                      external
                                      override
                                      whenNotPaused
                                      returns (bool)
                                  {
                                      _decreaseAllowance(msg.sender, spender, decrement);
                                      return true;
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              pragma solidity >=0.6.2 <0.8.0;
                              /**
                               * @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
                                   * ====
                                   */
                                  function isContract(address account) internal view returns (bool) {
                                      // This method relies on extcodesize, which returns 0 for contracts in
                                      // construction, since the code is only stored at the end of the
                                      // constructor execution.
                                      uint256 size;
                                      // solhint-disable-next-line no-inline-assembly
                                      assembly { size := extcodesize(account) }
                                      return size > 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");
                                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                                      (bool success, ) = recipient.call{ value: amount }("");
                                      require(success, "Address: unable to send value, recipient may have reverted");
                                  }
                                  /**
                                   * @dev Performs a Solidity function call using a low level `call`. A
                                   * plain`call` is an unsafe replacement for a function call: use this
                                   * function instead.
                                   *
                                   * If `target` reverts with a revert reason, it is bubbled up by this
                                   * function (like regular Solidity function calls).
                                   *
                                   * Returns the raw returned data. To convert to the expected return value,
                                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                                   *
                                   * Requirements:
                                   *
                                   * - `target` must be a contract.
                                   * - calling `target` with `data` must not revert.
                                   *
                                   * _Available since v3.1._
                                   */
                                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                    return functionCall(target, data, "Address: low-level call failed");
                                  }
                                  /**
                                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                                   * `errorMessage` as a fallback revert reason when `target` reverts.
                                   *
                                   * _Available since v3.1._
                                   */
                                  function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                                      return functionCallWithValue(target, data, 0, errorMessage);
                                  }
                                  /**
                                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                   * but also transferring `value` wei to `target`.
                                   *
                                   * Requirements:
                                   *
                                   * - the calling contract must have an ETH balance of at least `value`.
                                   * - the called Solidity function must be `payable`.
                                   *
                                   * _Available since v3.1._
                                   */
                                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                                  }
                                  /**
                                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                                   *
                                   * _Available since v3.1._
                                   */
                                  function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                                      require(address(this).balance >= value, "Address: insufficient balance for call");
                                      require(isContract(target), "Address: call to non-contract");
                                      // solhint-disable-next-line avoid-low-level-calls
                                      (bool success, bytes memory returndata) = target.call{ value: value }(data);
                                      return _verifyCallResult(success, returndata, errorMessage);
                                  }
                                  /**
                                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                   * but performing a static call.
                                   *
                                   * _Available since v3.3._
                                   */
                                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                      return functionStaticCall(target, data, "Address: low-level static call failed");
                                  }
                                  /**
                                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                                   * but performing a static call.
                                   *
                                   * _Available since v3.3._
                                   */
                                  function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                                      require(isContract(target), "Address: static call to non-contract");
                                      // solhint-disable-next-line avoid-low-level-calls
                                      (bool success, bytes memory returndata) = target.staticcall(data);
                                      return _verifyCallResult(success, returndata, errorMessage);
                                  }
                                  /**
                                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                   * but performing a delegate call.
                                   *
                                   * _Available since v3.4._
                                   */
                                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                                  }
                                  /**
                                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                                   * but performing a delegate call.
                                   *
                                   * _Available since v3.4._
                                   */
                                  function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                                      require(isContract(target), "Address: delegate call to non-contract");
                                      // solhint-disable-next-line avoid-low-level-calls
                                      (bool success, bytes memory returndata) = target.delegatecall(data);
                                      return _verifyCallResult(success, returndata, errorMessage);
                                  }
                                  function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                                      if (success) {
                                          return returndata;
                                      } else {
                                          // Look for revert reason and bubble it up if present
                                          if (returndata.length > 0) {
                                              // The easiest way to bubble the revert reason is using memory via assembly
                                              // solhint-disable-next-line no-inline-assembly
                                              assembly {
                                                  let returndata_size := mload(returndata)
                                                  revert(add(32, returndata), returndata_size)
                                              }
                                          } else {
                                              revert(errorMessage);
                                          }
                                      }
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              pragma solidity >=0.6.0 <0.8.0;
                              import "./IERC20.sol";
                              import "../../math/SafeMath.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 SafeMath for uint256;
                                  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'
                                      // solhint-disable-next-line max-line-length
                                      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).add(value);
                                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                                  }
                                  function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                                      uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                                  }
                                  /**
                                   * @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
                                          // solhint-disable-next-line max-line-length
                                          require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                                      }
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              pragma solidity >=0.6.0 <0.8.0;
                              /**
                               * @dev Interface of the ERC20 standard as defined in the EIP.
                               */
                              interface IERC20 {
                                  /**
                                   * @dev Returns the amount of tokens in existence.
                                   */
                                  function totalSupply() external view returns (uint256);
                                  /**
                                   * @dev Returns the amount of tokens owned by `account`.
                                   */
                                  function balanceOf(address account) external view returns (uint256);
                                  /**
                                   * @dev Moves `amount` tokens from the caller's account to `recipient`.
                                   *
                                   * Returns a boolean value indicating whether the operation succeeded.
                                   *
                                   * Emits a {Transfer} event.
                                   */
                                  function transfer(address recipient, uint256 amount) external returns (bool);
                                  /**
                                   * @dev Returns the remaining number of tokens that `spender` will be
                                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                                   * zero by default.
                                   *
                                   * This value changes when {approve} or {transferFrom} are called.
                                   */
                                  function allowance(address owner, address spender) external view returns (uint256);
                                  /**
                                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                                   *
                                   * Returns a boolean value indicating whether the operation succeeded.
                                   *
                                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                                   * that someone may use both the old and the new allowance by unfortunate
                                   * transaction ordering. One possible solution to mitigate this race
                                   * condition is to first reduce the spender's allowance to 0 and set the
                                   * desired value afterwards:
                                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                                   *
                                   * Emits an {Approval} event.
                                   */
                                  function approve(address spender, uint256 amount) external returns (bool);
                                  /**
                                   * @dev Moves `amount` tokens from `sender` to `recipient` using the
                                   * allowance mechanism. `amount` is then deducted from the caller's
                                   * allowance.
                                   *
                                   * Returns a boolean value indicating whether the operation succeeded.
                                   *
                                   * Emits a {Transfer} event.
                                   */
                                  function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                                  /**
                                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                                   * another (`to`).
                                   *
                                   * Note that `value` may be zero.
                                   */
                                  event Transfer(address indexed from, address indexed to, uint256 value);
                                  /**
                                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                                   * a call to {approve}. `value` is the new allowance.
                                   */
                                  event Approval(address indexed owner, address indexed spender, uint256 value);
                              }
                              // SPDX-License-Identifier: MIT
                              pragma solidity >=0.6.0 <0.8.0;
                              /**
                               * @dev Wrappers over Solidity's arithmetic operations with added overflow
                               * checks.
                               *
                               * Arithmetic operations in Solidity wrap on overflow. This can easily result
                               * in bugs, because programmers usually assume that an overflow raises an
                               * error, which is the standard behavior in high level programming languages.
                               * `SafeMath` restores this intuition by reverting the transaction when an
                               * operation overflows.
                               *
                               * Using this library instead of the unchecked operations eliminates an entire
                               * class of bugs, so it's recommended to use it always.
                               */
                              library SafeMath {
                                  /**
                                   * @dev Returns the addition of two unsigned integers, with an overflow flag.
                                   *
                                   * _Available since v3.4._
                                   */
                                  function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                      uint256 c = a + b;
                                      if (c < a) return (false, 0);
                                      return (true, c);
                                  }
                                  /**
                                   * @dev Returns the substraction of two unsigned integers, with an overflow flag.
                                   *
                                   * _Available since v3.4._
                                   */
                                  function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                      if (b > a) return (false, 0);
                                      return (true, a - b);
                                  }
                                  /**
                                   * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
                                   *
                                   * _Available since v3.4._
                                   */
                                  function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                      // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                                      // benefit is lost if 'b' is also tested.
                                      // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                                      if (a == 0) return (true, 0);
                                      uint256 c = a * b;
                                      if (c / a != b) return (false, 0);
                                      return (true, c);
                                  }
                                  /**
                                   * @dev Returns the division of two unsigned integers, with a division by zero flag.
                                   *
                                   * _Available since v3.4._
                                   */
                                  function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                      if (b == 0) return (false, 0);
                                      return (true, a / b);
                                  }
                                  /**
                                   * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
                                   *
                                   * _Available since v3.4._
                                   */
                                  function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                                      if (b == 0) return (false, 0);
                                      return (true, a % b);
                                  }
                                  /**
                                   * @dev Returns the addition of two unsigned integers, reverting on
                                   * overflow.
                                   *
                                   * Counterpart to Solidity's `+` operator.
                                   *
                                   * Requirements:
                                   *
                                   * - Addition cannot overflow.
                                   */
                                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                                      uint256 c = a + b;
                                      require(c >= a, "SafeMath: addition overflow");
                                      return c;
                                  }
                                  /**
                                   * @dev Returns the subtraction of two unsigned integers, reverting on
                                   * overflow (when the result is negative).
                                   *
                                   * Counterpart to Solidity's `-` operator.
                                   *
                                   * Requirements:
                                   *
                                   * - Subtraction cannot overflow.
                                   */
                                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                                      require(b <= a, "SafeMath: subtraction overflow");
                                      return a - b;
                                  }
                                  /**
                                   * @dev Returns the multiplication of two unsigned integers, reverting on
                                   * overflow.
                                   *
                                   * Counterpart to Solidity's `*` operator.
                                   *
                                   * Requirements:
                                   *
                                   * - Multiplication cannot overflow.
                                   */
                                  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                                      if (a == 0) return 0;
                                      uint256 c = a * b;
                                      require(c / a == b, "SafeMath: multiplication overflow");
                                      return c;
                                  }
                                  /**
                                   * @dev Returns the integer division of two unsigned integers, reverting on
                                   * division by zero. The result is rounded towards zero.
                                   *
                                   * Counterpart to Solidity's `/` operator. Note: this function uses a
                                   * `revert` opcode (which leaves remaining gas untouched) while Solidity
                                   * uses an invalid opcode to revert (consuming all remaining gas).
                                   *
                                   * Requirements:
                                   *
                                   * - The divisor cannot be zero.
                                   */
                                  function div(uint256 a, uint256 b) internal pure returns (uint256) {
                                      require(b > 0, "SafeMath: division by zero");
                                      return a / b;
                                  }
                                  /**
                                   * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                                   * reverting when dividing by zero.
                                   *
                                   * Counterpart to Solidity's `%` operator. This function uses a `revert`
                                   * opcode (which leaves remaining gas untouched) while Solidity uses an
                                   * invalid opcode to revert (consuming all remaining gas).
                                   *
                                   * Requirements:
                                   *
                                   * - The divisor cannot be zero.
                                   */
                                  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                                      require(b > 0, "SafeMath: modulo by zero");
                                      return a % b;
                                  }
                                  /**
                                   * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                                   * overflow (when the result is negative).
                                   *
                                   * CAUTION: This function is deprecated because it requires allocating memory for the error
                                   * message unnecessarily. For custom revert reasons use {trySub}.
                                   *
                                   * Counterpart to Solidity's `-` operator.
                                   *
                                   * Requirements:
                                   *
                                   * - Subtraction cannot overflow.
                                   */
                                  function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                      require(b <= a, errorMessage);
                                      return a - b;
                                  }
                                  /**
                                   * @dev Returns the integer division of two unsigned integers, reverting with custom message on
                                   * division by zero. The result is rounded towards zero.
                                   *
                                   * CAUTION: This function is deprecated because it requires allocating memory for the error
                                   * message unnecessarily. For custom revert reasons use {tryDiv}.
                                   *
                                   * Counterpart to Solidity's `/` operator. Note: this function uses a
                                   * `revert` opcode (which leaves remaining gas untouched) while Solidity
                                   * uses an invalid opcode to revert (consuming all remaining gas).
                                   *
                                   * Requirements:
                                   *
                                   * - The divisor cannot be zero.
                                   */
                                  function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                      require(b > 0, errorMessage);
                                      return a / b;
                                  }
                                  /**
                                   * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                                   * reverting with custom message when dividing by zero.
                                   *
                                   * CAUTION: This function is deprecated because it requires allocating memory for the error
                                   * message unnecessarily. For custom revert reasons use {tryMod}.
                                   *
                                   * Counterpart to Solidity's `%` operator. This function uses a `revert`
                                   * opcode (which leaves remaining gas untouched) while Solidity uses an
                                   * invalid opcode to revert (consuming all remaining gas).
                                   *
                                   * Requirements:
                                   *
                                   * - The divisor cannot be zero.
                                   */
                                  function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                      require(b > 0, errorMessage);
                                      return a % b;
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              import { FiatTokenV2 } from "./FiatTokenV2.sol";
                              // solhint-disable func-name-mixedcase
                              /**
                               * @title FiatToken V2.1
                               * @notice ERC20 Token backed by fiat reserves, version 2.1
                               */
                              contract FiatTokenV2_1 is FiatTokenV2 {
                                  /**
                                   * @notice Initialize v2.1
                                   * @param lostAndFound  The address to which the locked funds are sent
                                   */
                                  function initializeV2_1(address lostAndFound) external {
                                      // solhint-disable-next-line reason-string
                                      require(_initializedVersion == 1);
                                      uint256 lockedAmount = _balanceOf(address(this));
                                      if (lockedAmount > 0) {
                                          _transfer(address(this), lostAndFound, lockedAmount);
                                      }
                                      _blacklist(address(this));
                                      _initializedVersion = 2;
                                  }
                                  /**
                                   * @notice Version string for the EIP712 domain separator
                                   * @return Version string
                                   */
                                  function version() external pure returns (string memory) {
                                      return "2";
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              import { FiatTokenV1_1 } from "../v1.1/FiatTokenV1_1.sol";
                              import { EIP712 } from "../util/EIP712.sol";
                              import { EIP3009 } from "./EIP3009.sol";
                              import { EIP2612 } from "./EIP2612.sol";
                              /**
                               * @title FiatToken V2
                               * @notice ERC20 Token backed by fiat reserves, version 2
                               */
                              contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 {
                                  uint8 internal _initializedVersion;
                                  /**
                                   * @notice Initialize v2
                                   * @param newName   New token name
                                   */
                                  function initializeV2(string calldata newName) external {
                                      // solhint-disable-next-line reason-string
                                      require(initialized && _initializedVersion == 0);
                                      name = newName;
                                      _DEPRECATED_CACHED_DOMAIN_SEPARATOR = EIP712.makeDomainSeparator(
                                          newName,
                                          "2"
                                      );
                                      _initializedVersion = 1;
                                  }
                                  /**
                                   * @notice Increase the allowance by a given increment
                                   * @param spender   Spender's address
                                   * @param increment Amount of increase in allowance
                                   * @return True if successful
                                   */
                                  function increaseAllowance(address spender, uint256 increment)
                                      external
                                      virtual
                                      whenNotPaused
                                      notBlacklisted(msg.sender)
                                      notBlacklisted(spender)
                                      returns (bool)
                                  {
                                      _increaseAllowance(msg.sender, spender, increment);
                                      return true;
                                  }
                                  /**
                                   * @notice Decrease the allowance by a given decrement
                                   * @param spender   Spender's address
                                   * @param decrement Amount of decrease in allowance
                                   * @return True if successful
                                   */
                                  function decreaseAllowance(address spender, uint256 decrement)
                                      external
                                      virtual
                                      whenNotPaused
                                      notBlacklisted(msg.sender)
                                      notBlacklisted(spender)
                                      returns (bool)
                                  {
                                      _decreaseAllowance(msg.sender, spender, decrement);
                                      return true;
                                  }
                                  /**
                                   * @notice Execute a transfer with a signed authorization
                                   * @param from          Payer's address (Authorizer)
                                   * @param to            Payee's address
                                   * @param value         Amount to be transferred
                                   * @param validAfter    The time after which this is valid (unix time)
                                   * @param validBefore   The time before which this is valid (unix time)
                                   * @param nonce         Unique nonce
                                   * @param v             v of the signature
                                   * @param r             r of the signature
                                   * @param s             s of the signature
                                   */
                                  function transferWithAuthorization(
                                      address from,
                                      address to,
                                      uint256 value,
                                      uint256 validAfter,
                                      uint256 validBefore,
                                      bytes32 nonce,
                                      uint8 v,
                                      bytes32 r,
                                      bytes32 s
                                  ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                                      _transferWithAuthorization(
                                          from,
                                          to,
                                          value,
                                          validAfter,
                                          validBefore,
                                          nonce,
                                          v,
                                          r,
                                          s
                                      );
                                  }
                                  /**
                                   * @notice Receive a transfer with a signed authorization from the payer
                                   * @dev This has an additional check to ensure that the payee's address
                                   * matches the caller of this function to prevent front-running attacks.
                                   * @param from          Payer's address (Authorizer)
                                   * @param to            Payee's address
                                   * @param value         Amount to be transferred
                                   * @param validAfter    The time after which this is valid (unix time)
                                   * @param validBefore   The time before which this is valid (unix time)
                                   * @param nonce         Unique nonce
                                   * @param v             v of the signature
                                   * @param r             r of the signature
                                   * @param s             s of the signature
                                   */
                                  function receiveWithAuthorization(
                                      address from,
                                      address to,
                                      uint256 value,
                                      uint256 validAfter,
                                      uint256 validBefore,
                                      bytes32 nonce,
                                      uint8 v,
                                      bytes32 r,
                                      bytes32 s
                                  ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                                      _receiveWithAuthorization(
                                          from,
                                          to,
                                          value,
                                          validAfter,
                                          validBefore,
                                          nonce,
                                          v,
                                          r,
                                          s
                                      );
                                  }
                                  /**
                                   * @notice Attempt to cancel an authorization
                                   * @dev Works only if the authorization is not yet used.
                                   * @param authorizer    Authorizer's address
                                   * @param nonce         Nonce of the authorization
                                   * @param v             v of the signature
                                   * @param r             r of the signature
                                   * @param s             s of the signature
                                   */
                                  function cancelAuthorization(
                                      address authorizer,
                                      bytes32 nonce,
                                      uint8 v,
                                      bytes32 r,
                                      bytes32 s
                                  ) external whenNotPaused {
                                      _cancelAuthorization(authorizer, nonce, v, r, s);
                                  }
                                  /**
                                   * @notice Update allowance with a signed permit
                                   * @param owner       Token owner's address (Authorizer)
                                   * @param spender     Spender's address
                                   * @param value       Amount of allowance
                                   * @param deadline    The time at which the signature expires (unix time), or max uint256 value to signal no expiration
                                   * @param v           v of the signature
                                   * @param r           r of the signature
                                   * @param s           s of the signature
                                   */
                                  function permit(
                                      address owner,
                                      address spender,
                                      uint256 value,
                                      uint256 deadline,
                                      uint8 v,
                                      bytes32 r,
                                      bytes32 s
                                  )
                                      external
                                      virtual
                                      whenNotPaused
                                      notBlacklisted(owner)
                                      notBlacklisted(spender)
                                  {
                                      _permit(owner, spender, value, deadline, v, r, s);
                                  }
                                  /**
                                   * @dev Internal function to increase the allowance by a given increment
                                   * @param owner     Token owner's address
                                   * @param spender   Spender's address
                                   * @param increment Amount of increase
                                   */
                                  function _increaseAllowance(
                                      address owner,
                                      address spender,
                                      uint256 increment
                                  ) internal override {
                                      _approve(owner, spender, allowed[owner][spender].add(increment));
                                  }
                                  /**
                                   * @dev Internal function to decrease the allowance by a given decrement
                                   * @param owner     Token owner's address
                                   * @param spender   Spender's address
                                   * @param decrement Amount of decrease
                                   */
                                  function _decreaseAllowance(
                                      address owner,
                                      address spender,
                                      uint256 decrement
                                  ) internal override {
                                      _approve(
                                          owner,
                                          spender,
                                          allowed[owner][spender].sub(
                                              decrement,
                                              "ERC20: decreased allowance below zero"
                                          )
                                      );
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              // solhint-disable func-name-mixedcase
                              /**
                               * @title EIP712 Domain
                               */
                              contract EIP712Domain {
                                  // was originally DOMAIN_SEPARATOR
                                  // but that has been moved to a method so we can override it in V2_2+
                                  bytes32 internal _DEPRECATED_CACHED_DOMAIN_SEPARATOR;
                                  /**
                                   * @notice Get the EIP712 Domain Separator.
                                   * @return The bytes32 EIP712 domain separator.
                                   */
                                  function DOMAIN_SEPARATOR() external view returns (bytes32) {
                                      return _domainSeparator();
                                  }
                                  /**
                                   * @dev Internal method to get the EIP712 Domain Separator.
                                   * @return The bytes32 EIP712 domain separator.
                                   */
                                  function _domainSeparator() internal virtual view returns (bytes32) {
                                      return _DEPRECATED_CACHED_DOMAIN_SEPARATOR;
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              import { AbstractFiatTokenV2 } from "./AbstractFiatTokenV2.sol";
                              import { EIP712Domain } from "./EIP712Domain.sol";
                              import { SignatureChecker } from "../util/SignatureChecker.sol";
                              import { MessageHashUtils } from "../util/MessageHashUtils.sol";
                              /**
                               * @title EIP-3009
                               * @notice Provide internal implementation for gas-abstracted transfers
                               * @dev Contracts that inherit from this must wrap these with publicly
                               * accessible functions, optionally adding modifiers where necessary
                               */
                              abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain {
                                  // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
                                  bytes32
                                      public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;
                                  // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
                                  bytes32
                                      public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8;
                                  // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)")
                                  bytes32
                                      public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429;
                                  /**
                                   * @dev authorizer address => nonce => bool (true if nonce is used)
                                   */
                                  mapping(address => mapping(bytes32 => bool)) private _authorizationStates;
                                  event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);
                                  event AuthorizationCanceled(
                                      address indexed authorizer,
                                      bytes32 indexed nonce
                                  );
                                  /**
                                   * @notice Returns the state of an authorization
                                   * @dev Nonces are randomly generated 32-byte data unique to the
                                   * authorizer's address
                                   * @param authorizer    Authorizer's address
                                   * @param nonce         Nonce of the authorization
                                   * @return True if the nonce is used
                                   */
                                  function authorizationState(address authorizer, bytes32 nonce)
                                      external
                                      view
                                      returns (bool)
                                  {
                                      return _authorizationStates[authorizer][nonce];
                                  }
                                  /**
                                   * @notice Execute a transfer with a signed authorization
                                   * @param from          Payer's address (Authorizer)
                                   * @param to            Payee's address
                                   * @param value         Amount to be transferred
                                   * @param validAfter    The time after which this is valid (unix time)
                                   * @param validBefore   The time before which this is valid (unix time)
                                   * @param nonce         Unique nonce
                                   * @param v             v of the signature
                                   * @param r             r of the signature
                                   * @param s             s of the signature
                                   */
                                  function _transferWithAuthorization(
                                      address from,
                                      address to,
                                      uint256 value,
                                      uint256 validAfter,
                                      uint256 validBefore,
                                      bytes32 nonce,
                                      uint8 v,
                                      bytes32 r,
                                      bytes32 s
                                  ) internal {
                                      _transferWithAuthorization(
                                          from,
                                          to,
                                          value,
                                          validAfter,
                                          validBefore,
                                          nonce,
                                          abi.encodePacked(r, s, v)
                                      );
                                  }
                                  /**
                                   * @notice Execute a transfer with a signed authorization
                                   * @dev EOA wallet signatures should be packed in the order of r, s, v.
                                   * @param from          Payer's address (Authorizer)
                                   * @param to            Payee's address
                                   * @param value         Amount to be transferred
                                   * @param validAfter    The time after which this is valid (unix time)
                                   * @param validBefore   The time before which this is valid (unix time)
                                   * @param nonce         Unique nonce
                                   * @param signature     Signature byte array produced by an EOA wallet or a contract wallet
                                   */
                                  function _transferWithAuthorization(
                                      address from,
                                      address to,
                                      uint256 value,
                                      uint256 validAfter,
                                      uint256 validBefore,
                                      bytes32 nonce,
                                      bytes memory signature
                                  ) internal {
                                      _requireValidAuthorization(from, nonce, validAfter, validBefore);
                                      _requireValidSignature(
                                          from,
                                          keccak256(
                                              abi.encode(
                                                  TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
                                                  from,
                                                  to,
                                                  value,
                                                  validAfter,
                                                  validBefore,
                                                  nonce
                                              )
                                          ),
                                          signature
                                      );
                                      _markAuthorizationAsUsed(from, nonce);
                                      _transfer(from, to, value);
                                  }
                                  /**
                                   * @notice Receive a transfer with a signed authorization from the payer
                                   * @dev This has an additional check to ensure that the payee's address
                                   * matches the caller of this function to prevent front-running attacks.
                                   * @param from          Payer's address (Authorizer)
                                   * @param to            Payee's address
                                   * @param value         Amount to be transferred
                                   * @param validAfter    The time after which this is valid (unix time)
                                   * @param validBefore   The time before which this is valid (unix time)
                                   * @param nonce         Unique nonce
                                   * @param v             v of the signature
                                   * @param r             r of the signature
                                   * @param s             s of the signature
                                   */
                                  function _receiveWithAuthorization(
                                      address from,
                                      address to,
                                      uint256 value,
                                      uint256 validAfter,
                                      uint256 validBefore,
                                      bytes32 nonce,
                                      uint8 v,
                                      bytes32 r,
                                      bytes32 s
                                  ) internal {
                                      _receiveWithAuthorization(
                                          from,
                                          to,
                                          value,
                                          validAfter,
                                          validBefore,
                                          nonce,
                                          abi.encodePacked(r, s, v)
                                      );
                                  }
                                  /**
                                   * @notice Receive a transfer with a signed authorization from the payer
                                   * @dev This has an additional check to ensure that the payee's address
                                   * matches the caller of this function to prevent front-running attacks.
                                   * EOA wallet signatures should be packed in the order of r, s, v.
                                   * @param from          Payer's address (Authorizer)
                                   * @param to            Payee's address
                                   * @param value         Amount to be transferred
                                   * @param validAfter    The time after which this is valid (unix time)
                                   * @param validBefore   The time before which this is valid (unix time)
                                   * @param nonce         Unique nonce
                                   * @param signature     Signature byte array produced by an EOA wallet or a contract wallet
                                   */
                                  function _receiveWithAuthorization(
                                      address from,
                                      address to,
                                      uint256 value,
                                      uint256 validAfter,
                                      uint256 validBefore,
                                      bytes32 nonce,
                                      bytes memory signature
                                  ) internal {
                                      require(to == msg.sender, "FiatTokenV2: caller must be the payee");
                                      _requireValidAuthorization(from, nonce, validAfter, validBefore);
                                      _requireValidSignature(
                                          from,
                                          keccak256(
                                              abi.encode(
                                                  RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
                                                  from,
                                                  to,
                                                  value,
                                                  validAfter,
                                                  validBefore,
                                                  nonce
                                              )
                                          ),
                                          signature
                                      );
                                      _markAuthorizationAsUsed(from, nonce);
                                      _transfer(from, to, value);
                                  }
                                  /**
                                   * @notice Attempt to cancel an authorization
                                   * @param authorizer    Authorizer's address
                                   * @param nonce         Nonce of the authorization
                                   * @param v             v of the signature
                                   * @param r             r of the signature
                                   * @param s             s of the signature
                                   */
                                  function _cancelAuthorization(
                                      address authorizer,
                                      bytes32 nonce,
                                      uint8 v,
                                      bytes32 r,
                                      bytes32 s
                                  ) internal {
                                      _cancelAuthorization(authorizer, nonce, abi.encodePacked(r, s, v));
                                  }
                                  /**
                                   * @notice Attempt to cancel an authorization
                                   * @dev EOA wallet signatures should be packed in the order of r, s, v.
                                   * @param authorizer    Authorizer's address
                                   * @param nonce         Nonce of the authorization
                                   * @param signature     Signature byte array produced by an EOA wallet or a contract wallet
                                   */
                                  function _cancelAuthorization(
                                      address authorizer,
                                      bytes32 nonce,
                                      bytes memory signature
                                  ) internal {
                                      _requireUnusedAuthorization(authorizer, nonce);
                                      _requireValidSignature(
                                          authorizer,
                                          keccak256(
                                              abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer, nonce)
                                          ),
                                          signature
                                      );
                                      _authorizationStates[authorizer][nonce] = true;
                                      emit AuthorizationCanceled(authorizer, nonce);
                                  }
                                  /**
                                   * @notice Validates that signature against input data struct
                                   * @param signer        Signer's address
                                   * @param dataHash      Hash of encoded data struct
                                   * @param signature     Signature byte array produced by an EOA wallet or a contract wallet
                                   */
                                  function _requireValidSignature(
                                      address signer,
                                      bytes32 dataHash,
                                      bytes memory signature
                                  ) private view {
                                      require(
                                          SignatureChecker.isValidSignatureNow(
                                              signer,
                                              MessageHashUtils.toTypedDataHash(_domainSeparator(), dataHash),
                                              signature
                                          ),
                                          "FiatTokenV2: invalid signature"
                                      );
                                  }
                                  /**
                                   * @notice Check that an authorization is unused
                                   * @param authorizer    Authorizer's address
                                   * @param nonce         Nonce of the authorization
                                   */
                                  function _requireUnusedAuthorization(address authorizer, bytes32 nonce)
                                      private
                                      view
                                  {
                                      require(
                                          !_authorizationStates[authorizer][nonce],
                                          "FiatTokenV2: authorization is used or canceled"
                                      );
                                  }
                                  /**
                                   * @notice Check that authorization is valid
                                   * @param authorizer    Authorizer's address
                                   * @param nonce         Nonce of the authorization
                                   * @param validAfter    The time after which this is valid (unix time)
                                   * @param validBefore   The time before which this is valid (unix time)
                                   */
                                  function _requireValidAuthorization(
                                      address authorizer,
                                      bytes32 nonce,
                                      uint256 validAfter,
                                      uint256 validBefore
                                  ) private view {
                                      require(
                                          now > validAfter,
                                          "FiatTokenV2: authorization is not yet valid"
                                      );
                                      require(now < validBefore, "FiatTokenV2: authorization is expired");
                                      _requireUnusedAuthorization(authorizer, nonce);
                                  }
                                  /**
                                   * @notice Mark an authorization as used
                                   * @param authorizer    Authorizer's address
                                   * @param nonce         Nonce of the authorization
                                   */
                                  function _markAuthorizationAsUsed(address authorizer, bytes32 nonce)
                                      private
                                  {
                                      _authorizationStates[authorizer][nonce] = true;
                                      emit AuthorizationUsed(authorizer, nonce);
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              import { AbstractFiatTokenV2 } from "./AbstractFiatTokenV2.sol";
                              import { EIP712Domain } from "./EIP712Domain.sol";
                              import { MessageHashUtils } from "../util/MessageHashUtils.sol";
                              import { SignatureChecker } from "../util/SignatureChecker.sol";
                              /**
                               * @title EIP-2612
                               * @notice Provide internal implementation for gas-abstracted approvals
                               */
                              abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain {
                                  // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
                                  bytes32
                                      public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
                                  mapping(address => uint256) private _permitNonces;
                                  /**
                                   * @notice Nonces for permit
                                   * @param owner Token owner's address (Authorizer)
                                   * @return Next nonce
                                   */
                                  function nonces(address owner) external view returns (uint256) {
                                      return _permitNonces[owner];
                                  }
                                  /**
                                   * @notice Verify a signed approval permit and execute if valid
                                   * @param owner     Token owner's address (Authorizer)
                                   * @param spender   Spender's address
                                   * @param value     Amount of allowance
                                   * @param deadline  The time at which the signature expires (unix time), or max uint256 value to signal no expiration
                                   * @param v         v of the signature
                                   * @param r         r of the signature
                                   * @param s         s of the signature
                                   */
                                  function _permit(
                                      address owner,
                                      address spender,
                                      uint256 value,
                                      uint256 deadline,
                                      uint8 v,
                                      bytes32 r,
                                      bytes32 s
                                  ) internal {
                                      _permit(owner, spender, value, deadline, abi.encodePacked(r, s, v));
                                  }
                                  /**
                                   * @notice Verify a signed approval permit and execute if valid
                                   * @dev EOA wallet signatures should be packed in the order of r, s, v.
                                   * @param owner      Token owner's address (Authorizer)
                                   * @param spender    Spender's address
                                   * @param value      Amount of allowance
                                   * @param deadline   The time at which the signature expires (unix time), or max uint256 value to signal no expiration
                                   * @param signature  Signature byte array signed by an EOA wallet or a contract wallet
                                   */
                                  function _permit(
                                      address owner,
                                      address spender,
                                      uint256 value,
                                      uint256 deadline,
                                      bytes memory signature
                                  ) internal {
                                      require(
                                          deadline == type(uint256).max || deadline >= now,
                                          "FiatTokenV2: permit is expired"
                                      );
                                      bytes32 typedDataHash = MessageHashUtils.toTypedDataHash(
                                          _domainSeparator(),
                                          keccak256(
                                              abi.encode(
                                                  PERMIT_TYPEHASH,
                                                  owner,
                                                  spender,
                                                  value,
                                                  _permitNonces[owner]++,
                                                  deadline
                                              )
                                          )
                                      );
                                      require(
                                          SignatureChecker.isValidSignatureNow(
                                              owner,
                                              typedDataHash,
                                              signature
                                          ),
                                          "EIP2612: invalid signature"
                                      );
                                      _approve(owner, spender, value);
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              import { AbstractFiatTokenV1 } from "../v1/AbstractFiatTokenV1.sol";
                              abstract contract AbstractFiatTokenV2 is AbstractFiatTokenV1 {
                                  function _increaseAllowance(
                                      address owner,
                                      address spender,
                                      uint256 increment
                                  ) internal virtual;
                                  function _decreaseAllowance(
                                      address owner,
                                      address spender,
                                      uint256 decrement
                                  ) internal virtual;
                              }
                              /**
                               * SPDX-License-Identifier: MIT
                               *
                               * Copyright (c) 2016 Smart Contract Solutions, Inc.
                               * Copyright (c) 2018-2020 CENTRE SECZ
                               *
                               * Permission is hereby granted, free of charge, to any person obtaining a copy
                               * of this software and associated documentation files (the "Software"), to deal
                               * in the Software without restriction, including without limitation the rights
                               * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                               * copies of the Software, and to permit persons to whom the Software is
                               * furnished to do so, subject to the following conditions:
                               *
                               * The above copyright notice and this permission notice shall be included in
                               * copies or substantial portions of the Software.
                               *
                               * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                               * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                               * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                               * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                               * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                               * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                               * SOFTWARE.
                               */
                              pragma solidity 0.6.12;
                              import { Ownable } from "./Ownable.sol";
                              /**
                               * @notice Base contract which allows children to implement an emergency stop
                               * mechanism
                               * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/feb665136c0dae9912e08397c1a21c4af3651ef3/contracts/lifecycle/Pausable.sol
                               * Modifications:
                               * 1. Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018)
                               * 2. Removed whenNotPause/whenPaused from pause/unpause (6/14/2018)
                               * 3. Removed whenPaused (6/14/2018)
                               * 4. Switches ownable library to use ZeppelinOS (7/12/18)
                               * 5. Remove constructor (7/13/18)
                               * 6. Reformat, conform to Solidity 0.6 syntax and add error messages (5/13/20)
                               * 7. Make public functions external (5/27/20)
                               */
                              contract Pausable is Ownable {
                                  event Pause();
                                  event Unpause();
                                  event PauserChanged(address indexed newAddress);
                                  address public pauser;
                                  bool public paused = false;
                                  /**
                                   * @dev Modifier to make a function callable only when the contract is not paused.
                                   */
                                  modifier whenNotPaused() {
                                      require(!paused, "Pausable: paused");
                                      _;
                                  }
                                  /**
                                   * @dev throws if called by any account other than the pauser
                                   */
                                  modifier onlyPauser() {
                                      require(msg.sender == pauser, "Pausable: caller is not the pauser");
                                      _;
                                  }
                                  /**
                                   * @dev called by the owner to pause, triggers stopped state
                                   */
                                  function pause() external onlyPauser {
                                      paused = true;
                                      emit Pause();
                                  }
                                  /**
                                   * @dev called by the owner to unpause, returns to normal state
                                   */
                                  function unpause() external onlyPauser {
                                      paused = false;
                                      emit Unpause();
                                  }
                                  /**
                                   * @notice Updates the pauser address.
                                   * @param _newPauser The address of the new pauser.
                                   */
                                  function updatePauser(address _newPauser) external onlyOwner {
                                      require(
                                          _newPauser != address(0),
                                          "Pausable: new pauser is the zero address"
                                      );
                                      pauser = _newPauser;
                                      emit PauserChanged(pauser);
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: MIT
                               *
                               * Copyright (c) 2018 zOS Global Limited.
                               * Copyright (c) 2018-2020 CENTRE SECZ
                               *
                               * Permission is hereby granted, free of charge, to any person obtaining a copy
                               * of this software and associated documentation files (the "Software"), to deal
                               * in the Software without restriction, including without limitation the rights
                               * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                               * copies of the Software, and to permit persons to whom the Software is
                               * furnished to do so, subject to the following conditions:
                               *
                               * The above copyright notice and this permission notice shall be included in
                               * copies or substantial portions of the Software.
                               *
                               * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                               * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                               * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                               * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                               * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                               * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                               * SOFTWARE.
                               */
                              pragma solidity 0.6.12;
                              /**
                               * @notice The Ownable contract has an owner address, and provides basic
                               * authorization control functions
                               * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-labs/blob/3887ab77b8adafba4a26ace002f3a684c1a3388b/upgradeability_ownership/contracts/ownership/Ownable.sol
                               * Modifications:
                               * 1. Consolidate OwnableStorage into this contract (7/13/18)
                               * 2. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20)
                               * 3. Make public functions external (5/27/20)
                               */
                              contract Ownable {
                                  // Owner of the contract
                                  address private _owner;
                                  /**
                                   * @dev Event to show ownership has been transferred
                                   * @param previousOwner representing the address of the previous owner
                                   * @param newOwner representing the address of the new owner
                                   */
                                  event OwnershipTransferred(address previousOwner, address newOwner);
                                  /**
                                   * @dev The constructor sets the original owner of the contract to the sender account.
                                   */
                                  constructor() public {
                                      setOwner(msg.sender);
                                  }
                                  /**
                                   * @dev Tells the address of the owner
                                   * @return the address of the owner
                                   */
                                  function owner() external view returns (address) {
                                      return _owner;
                                  }
                                  /**
                                   * @dev Sets a new owner address
                                   */
                                  function setOwner(address newOwner) internal {
                                      _owner = newOwner;
                                  }
                                  /**
                                   * @dev Throws if called by any account other than the owner.
                                   */
                                  modifier onlyOwner() {
                                      require(msg.sender == _owner, "Ownable: caller is not the owner");
                                      _;
                                  }
                                  /**
                                   * @dev Allows the current owner to transfer control of the contract to a newOwner.
                                   * @param newOwner The address to transfer ownership to.
                                   */
                                  function transferOwnership(address newOwner) external onlyOwner {
                                      require(
                                          newOwner != address(0),
                                          "Ownable: new owner is the zero address"
                                      );
                                      emit OwnershipTransferred(_owner, newOwner);
                                      setOwner(newOwner);
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
                              import { AbstractFiatTokenV1 } from "./AbstractFiatTokenV1.sol";
                              import { Ownable } from "./Ownable.sol";
                              import { Pausable } from "./Pausable.sol";
                              import { Blacklistable } from "./Blacklistable.sol";
                              /**
                               * @title FiatToken
                               * @dev ERC20 Token backed by fiat reserves
                               */
                              contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable {
                                  using SafeMath for uint256;
                                  string public name;
                                  string public symbol;
                                  uint8 public decimals;
                                  string public currency;
                                  address public masterMinter;
                                  bool internal initialized;
                                  /// @dev A mapping that stores the balance and blacklist states for a given address.
                                  /// The first bit defines whether the address is blacklisted (1 if blacklisted, 0 otherwise).
                                  /// The last 255 bits define the balance for the address.
                                  mapping(address => uint256) internal balanceAndBlacklistStates;
                                  mapping(address => mapping(address => uint256)) internal allowed;
                                  uint256 internal totalSupply_ = 0;
                                  mapping(address => bool) internal minters;
                                  mapping(address => uint256) internal minterAllowed;
                                  event Mint(address indexed minter, address indexed to, uint256 amount);
                                  event Burn(address indexed burner, uint256 amount);
                                  event MinterConfigured(address indexed minter, uint256 minterAllowedAmount);
                                  event MinterRemoved(address indexed oldMinter);
                                  event MasterMinterChanged(address indexed newMasterMinter);
                                  /**
                                   * @notice Initializes the fiat token contract.
                                   * @param tokenName       The name of the fiat token.
                                   * @param tokenSymbol     The symbol of the fiat token.
                                   * @param tokenCurrency   The fiat currency that the token represents.
                                   * @param tokenDecimals   The number of decimals that the token uses.
                                   * @param newMasterMinter The masterMinter address for the fiat token.
                                   * @param newPauser       The pauser address for the fiat token.
                                   * @param newBlacklister  The blacklister address for the fiat token.
                                   * @param newOwner        The owner of the fiat token.
                                   */
                                  function initialize(
                                      string memory tokenName,
                                      string memory tokenSymbol,
                                      string memory tokenCurrency,
                                      uint8 tokenDecimals,
                                      address newMasterMinter,
                                      address newPauser,
                                      address newBlacklister,
                                      address newOwner
                                  ) public {
                                      require(!initialized, "FiatToken: contract is already initialized");
                                      require(
                                          newMasterMinter != address(0),
                                          "FiatToken: new masterMinter is the zero address"
                                      );
                                      require(
                                          newPauser != address(0),
                                          "FiatToken: new pauser is the zero address"
                                      );
                                      require(
                                          newBlacklister != address(0),
                                          "FiatToken: new blacklister is the zero address"
                                      );
                                      require(
                                          newOwner != address(0),
                                          "FiatToken: new owner is the zero address"
                                      );
                                      name = tokenName;
                                      symbol = tokenSymbol;
                                      currency = tokenCurrency;
                                      decimals = tokenDecimals;
                                      masterMinter = newMasterMinter;
                                      pauser = newPauser;
                                      blacklister = newBlacklister;
                                      setOwner(newOwner);
                                      initialized = true;
                                  }
                                  /**
                                   * @dev Throws if called by any account other than a minter.
                                   */
                                  modifier onlyMinters() {
                                      require(minters[msg.sender], "FiatToken: caller is not a minter");
                                      _;
                                  }
                                  /**
                                   * @notice Mints fiat tokens to an address.
                                   * @param _to The address that will receive the minted tokens.
                                   * @param _amount The amount of tokens to mint. Must be less than or equal
                                   * to the minterAllowance of the caller.
                                   * @return True if the operation was successful.
                                   */
                                  function mint(address _to, uint256 _amount)
                                      external
                                      whenNotPaused
                                      onlyMinters
                                      notBlacklisted(msg.sender)
                                      notBlacklisted(_to)
                                      returns (bool)
                                  {
                                      require(_to != address(0), "FiatToken: mint to the zero address");
                                      require(_amount > 0, "FiatToken: mint amount not greater than 0");
                                      uint256 mintingAllowedAmount = minterAllowed[msg.sender];
                                      require(
                                          _amount <= mintingAllowedAmount,
                                          "FiatToken: mint amount exceeds minterAllowance"
                                      );
                                      totalSupply_ = totalSupply_.add(_amount);
                                      _setBalance(_to, _balanceOf(_to).add(_amount));
                                      minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount);
                                      emit Mint(msg.sender, _to, _amount);
                                      emit Transfer(address(0), _to, _amount);
                                      return true;
                                  }
                                  /**
                                   * @dev Throws if called by any account other than the masterMinter
                                   */
                                  modifier onlyMasterMinter() {
                                      require(
                                          msg.sender == masterMinter,
                                          "FiatToken: caller is not the masterMinter"
                                      );
                                      _;
                                  }
                                  /**
                                   * @notice Gets the minter allowance for an account.
                                   * @param minter The address to check.
                                   * @return The remaining minter allowance for the account.
                                   */
                                  function minterAllowance(address minter) external view returns (uint256) {
                                      return minterAllowed[minter];
                                  }
                                  /**
                                   * @notice Checks if an account is a minter.
                                   * @param account The address to check.
                                   * @return True if the account is a minter, false if the account is not a minter.
                                   */
                                  function isMinter(address account) external view returns (bool) {
                                      return minters[account];
                                  }
                                  /**
                                   * @notice Gets the remaining amount of fiat tokens a spender is allowed to transfer on
                                   * behalf of the token owner.
                                   * @param owner   The token owner's address.
                                   * @param spender The spender's address.
                                   * @return The remaining allowance.
                                   */
                                  function allowance(address owner, address spender)
                                      external
                                      override
                                      view
                                      returns (uint256)
                                  {
                                      return allowed[owner][spender];
                                  }
                                  /**
                                   * @notice Gets the totalSupply of the fiat token.
                                   * @return The totalSupply of the fiat token.
                                   */
                                  function totalSupply() external override view returns (uint256) {
                                      return totalSupply_;
                                  }
                                  /**
                                   * @notice Gets the fiat token balance of an account.
                                   * @param account  The address to check.
                                   * @return balance The fiat token balance of the account.
                                   */
                                  function balanceOf(address account)
                                      external
                                      override
                                      view
                                      returns (uint256)
                                  {
                                      return _balanceOf(account);
                                  }
                                  /**
                                   * @notice Sets a fiat token allowance for a spender to spend on behalf of the caller.
                                   * @param spender The spender's address.
                                   * @param value   The allowance amount.
                                   * @return True if the operation was successful.
                                   */
                                  function approve(address spender, uint256 value)
                                      external
                                      virtual
                                      override
                                      whenNotPaused
                                      notBlacklisted(msg.sender)
                                      notBlacklisted(spender)
                                      returns (bool)
                                  {
                                      _approve(msg.sender, spender, value);
                                      return true;
                                  }
                                  /**
                                   * @dev Internal function to set allowance.
                                   * @param owner     Token owner's address.
                                   * @param spender   Spender's address.
                                   * @param value     Allowance amount.
                                   */
                                  function _approve(
                                      address owner,
                                      address spender,
                                      uint256 value
                                  ) internal override {
                                      require(owner != address(0), "ERC20: approve from the zero address");
                                      require(spender != address(0), "ERC20: approve to the zero address");
                                      allowed[owner][spender] = value;
                                      emit Approval(owner, spender, value);
                                  }
                                  /**
                                   * @notice Transfers tokens from an address to another by spending the caller's allowance.
                                   * @dev The caller must have some fiat token allowance on the payer's tokens.
                                   * @param from  Payer's address.
                                   * @param to    Payee's address.
                                   * @param value Transfer amount.
                                   * @return True if the operation was successful.
                                   */
                                  function transferFrom(
                                      address from,
                                      address to,
                                      uint256 value
                                  )
                                      external
                                      override
                                      whenNotPaused
                                      notBlacklisted(msg.sender)
                                      notBlacklisted(from)
                                      notBlacklisted(to)
                                      returns (bool)
                                  {
                                      require(
                                          value <= allowed[from][msg.sender],
                                          "ERC20: transfer amount exceeds allowance"
                                      );
                                      _transfer(from, to, value);
                                      allowed[from][msg.sender] = allowed[from][msg.sender].sub(value);
                                      return true;
                                  }
                                  /**
                                   * @notice Transfers tokens from the caller.
                                   * @param to    Payee's address.
                                   * @param value Transfer amount.
                                   * @return True if the operation was successful.
                                   */
                                  function transfer(address to, uint256 value)
                                      external
                                      override
                                      whenNotPaused
                                      notBlacklisted(msg.sender)
                                      notBlacklisted(to)
                                      returns (bool)
                                  {
                                      _transfer(msg.sender, to, value);
                                      return true;
                                  }
                                  /**
                                   * @dev Internal function to process transfers.
                                   * @param from  Payer's address.
                                   * @param to    Payee's address.
                                   * @param value Transfer amount.
                                   */
                                  function _transfer(
                                      address from,
                                      address to,
                                      uint256 value
                                  ) internal override {
                                      require(from != address(0), "ERC20: transfer from the zero address");
                                      require(to != address(0), "ERC20: transfer to the zero address");
                                      require(
                                          value <= _balanceOf(from),
                                          "ERC20: transfer amount exceeds balance"
                                      );
                                      _setBalance(from, _balanceOf(from).sub(value));
                                      _setBalance(to, _balanceOf(to).add(value));
                                      emit Transfer(from, to, value);
                                  }
                                  /**
                                   * @notice Adds or updates a new minter with a mint allowance.
                                   * @param minter The address of the minter.
                                   * @param minterAllowedAmount The minting amount allowed for the minter.
                                   * @return True if the operation was successful.
                                   */
                                  function configureMinter(address minter, uint256 minterAllowedAmount)
                                      external
                                      whenNotPaused
                                      onlyMasterMinter
                                      returns (bool)
                                  {
                                      minters[minter] = true;
                                      minterAllowed[minter] = minterAllowedAmount;
                                      emit MinterConfigured(minter, minterAllowedAmount);
                                      return true;
                                  }
                                  /**
                                   * @notice Removes a minter.
                                   * @param minter The address of the minter to remove.
                                   * @return True if the operation was successful.
                                   */
                                  function removeMinter(address minter)
                                      external
                                      onlyMasterMinter
                                      returns (bool)
                                  {
                                      minters[minter] = false;
                                      minterAllowed[minter] = 0;
                                      emit MinterRemoved(minter);
                                      return true;
                                  }
                                  /**
                                   * @notice Allows a minter to burn some of its own tokens.
                                   * @dev The caller must be a minter, must not be blacklisted, and the amount to burn
                                   * should be less than or equal to the account's balance.
                                   * @param _amount the amount of tokens to be burned.
                                   */
                                  function burn(uint256 _amount)
                                      external
                                      whenNotPaused
                                      onlyMinters
                                      notBlacklisted(msg.sender)
                                  {
                                      uint256 balance = _balanceOf(msg.sender);
                                      require(_amount > 0, "FiatToken: burn amount not greater than 0");
                                      require(balance >= _amount, "FiatToken: burn amount exceeds balance");
                                      totalSupply_ = totalSupply_.sub(_amount);
                                      _setBalance(msg.sender, balance.sub(_amount));
                                      emit Burn(msg.sender, _amount);
                                      emit Transfer(msg.sender, address(0), _amount);
                                  }
                                  /**
                                   * @notice Updates the master minter address.
                                   * @param _newMasterMinter The address of the new master minter.
                                   */
                                  function updateMasterMinter(address _newMasterMinter) external onlyOwner {
                                      require(
                                          _newMasterMinter != address(0),
                                          "FiatToken: new masterMinter is the zero address"
                                      );
                                      masterMinter = _newMasterMinter;
                                      emit MasterMinterChanged(masterMinter);
                                  }
                                  /**
                                   * @inheritdoc Blacklistable
                                   */
                                  function _blacklist(address _account) internal override {
                                      _setBlacklistState(_account, true);
                                  }
                                  /**
                                   * @inheritdoc Blacklistable
                                   */
                                  function _unBlacklist(address _account) internal override {
                                      _setBlacklistState(_account, false);
                                  }
                                  /**
                                   * @dev Helper method that sets the blacklist state of an account.
                                   * @param _account         The address of the account.
                                   * @param _shouldBlacklist True if the account should be blacklisted, false if the account should be unblacklisted.
                                   */
                                  function _setBlacklistState(address _account, bool _shouldBlacklist)
                                      internal
                                      virtual
                                  {
                                      _deprecatedBlacklisted[_account] = _shouldBlacklist;
                                  }
                                  /**
                                   * @dev Helper method that sets the balance of an account.
                                   * @param _account The address of the account.
                                   * @param _balance The new fiat token balance of the account.
                                   */
                                  function _setBalance(address _account, uint256 _balance) internal virtual {
                                      balanceAndBlacklistStates[_account] = _balance;
                                  }
                                  /**
                                   * @inheritdoc Blacklistable
                                   */
                                  function _isBlacklisted(address _account)
                                      internal
                                      virtual
                                      override
                                      view
                                      returns (bool)
                                  {
                                      return _deprecatedBlacklisted[_account];
                                  }
                                  /**
                                   * @dev Helper method to obtain the balance of an account.
                                   * @param _account  The address of the account.
                                   * @return          The fiat token balance of the account.
                                   */
                                  function _balanceOf(address _account)
                                      internal
                                      virtual
                                      view
                                      returns (uint256)
                                  {
                                      return balanceAndBlacklistStates[_account];
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              import { Ownable } from "./Ownable.sol";
                              /**
                               * @title Blacklistable Token
                               * @dev Allows accounts to be blacklisted by a "blacklister" role
                               */
                              abstract contract Blacklistable is Ownable {
                                  address public blacklister;
                                  mapping(address => bool) internal _deprecatedBlacklisted;
                                  event Blacklisted(address indexed _account);
                                  event UnBlacklisted(address indexed _account);
                                  event BlacklisterChanged(address indexed newBlacklister);
                                  /**
                                   * @dev Throws if called by any account other than the blacklister.
                                   */
                                  modifier onlyBlacklister() {
                                      require(
                                          msg.sender == blacklister,
                                          "Blacklistable: caller is not the blacklister"
                                      );
                                      _;
                                  }
                                  /**
                                   * @dev Throws if argument account is blacklisted.
                                   * @param _account The address to check.
                                   */
                                  modifier notBlacklisted(address _account) {
                                      require(
                                          !_isBlacklisted(_account),
                                          "Blacklistable: account is blacklisted"
                                      );
                                      _;
                                  }
                                  /**
                                   * @notice Checks if account is blacklisted.
                                   * @param _account The address to check.
                                   * @return True if the account is blacklisted, false if the account is not blacklisted.
                                   */
                                  function isBlacklisted(address _account) external view returns (bool) {
                                      return _isBlacklisted(_account);
                                  }
                                  /**
                                   * @notice Adds account to blacklist.
                                   * @param _account The address to blacklist.
                                   */
                                  function blacklist(address _account) external onlyBlacklister {
                                      _blacklist(_account);
                                      emit Blacklisted(_account);
                                  }
                                  /**
                                   * @notice Removes account from blacklist.
                                   * @param _account The address to remove from the blacklist.
                                   */
                                  function unBlacklist(address _account) external onlyBlacklister {
                                      _unBlacklist(_account);
                                      emit UnBlacklisted(_account);
                                  }
                                  /**
                                   * @notice Updates the blacklister address.
                                   * @param _newBlacklister The address of the new blacklister.
                                   */
                                  function updateBlacklister(address _newBlacklister) external onlyOwner {
                                      require(
                                          _newBlacklister != address(0),
                                          "Blacklistable: new blacklister is the zero address"
                                      );
                                      blacklister = _newBlacklister;
                                      emit BlacklisterChanged(blacklister);
                                  }
                                  /**
                                   * @dev Checks if account is blacklisted.
                                   * @param _account The address to check.
                                   * @return true if the account is blacklisted, false otherwise.
                                   */
                                  function _isBlacklisted(address _account)
                                      internal
                                      virtual
                                      view
                                      returns (bool);
                                  /**
                                   * @dev Helper method that blacklists an account.
                                   * @param _account The address to blacklist.
                                   */
                                  function _blacklist(address _account) internal virtual;
                                  /**
                                   * @dev Helper method that unblacklists an account.
                                   * @param _account The address to unblacklist.
                                   */
                                  function _unBlacklist(address _account) internal virtual;
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                              abstract contract AbstractFiatTokenV1 is IERC20 {
                                  function _approve(
                                      address owner,
                                      address spender,
                                      uint256 value
                                  ) internal virtual;
                                  function _transfer(
                                      address from,
                                      address to,
                                      uint256 value
                                  ) internal virtual;
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              import { Ownable } from "../v1/Ownable.sol";
                              import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                              import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
                              contract Rescuable is Ownable {
                                  using SafeERC20 for IERC20;
                                  address private _rescuer;
                                  event RescuerChanged(address indexed newRescuer);
                                  /**
                                   * @notice Returns current rescuer
                                   * @return Rescuer's address
                                   */
                                  function rescuer() external view returns (address) {
                                      return _rescuer;
                                  }
                                  /**
                                   * @notice Revert if called by any account other than the rescuer.
                                   */
                                  modifier onlyRescuer() {
                                      require(msg.sender == _rescuer, "Rescuable: caller is not the rescuer");
                                      _;
                                  }
                                  /**
                                   * @notice Rescue ERC20 tokens locked up in this contract.
                                   * @param tokenContract ERC20 token contract address
                                   * @param to        Recipient address
                                   * @param amount    Amount to withdraw
                                   */
                                  function rescueERC20(
                                      IERC20 tokenContract,
                                      address to,
                                      uint256 amount
                                  ) external onlyRescuer {
                                      tokenContract.safeTransfer(to, amount);
                                  }
                                  /**
                                   * @notice Updates the rescuer address.
                                   * @param newRescuer The address of the new rescuer.
                                   */
                                  function updateRescuer(address newRescuer) external onlyOwner {
                                      require(
                                          newRescuer != address(0),
                                          "Rescuable: new rescuer is the zero address"
                                      );
                                      _rescuer = newRescuer;
                                      emit RescuerChanged(newRescuer);
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              import { FiatTokenV1 } from "../v1/FiatTokenV1.sol";
                              import { Rescuable } from "./Rescuable.sol";
                              /**
                               * @title FiatTokenV1_1
                               * @dev ERC20 Token backed by fiat reserves
                               */
                              contract FiatTokenV1_1 is FiatTokenV1, Rescuable {
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              import { ECRecover } from "./ECRecover.sol";
                              import { IERC1271 } from "../interface/IERC1271.sol";
                              /**
                               * @dev Signature verification helper that can be used instead of `ECRecover.recover` to seamlessly support both ECDSA
                               * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets.
                               *
                               * Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/SignatureChecker.sol
                               */
                              library SignatureChecker {
                                  /**
                                   * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
                                   * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECRecover.recover`.
                                   * @param signer        Address of the claimed signer
                                   * @param digest        Keccak-256 hash digest of the signed message
                                   * @param signature     Signature byte array associated with hash
                                   */
                                  function isValidSignatureNow(
                                      address signer,
                                      bytes32 digest,
                                      bytes memory signature
                                  ) external view returns (bool) {
                                      if (!isContract(signer)) {
                                          return ECRecover.recover(digest, signature) == signer;
                                      }
                                      return isValidERC1271SignatureNow(signer, digest, signature);
                                  }
                                  /**
                                   * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
                                   * against the signer smart contract using ERC1271.
                                   * @param signer        Address of the claimed signer
                                   * @param digest        Keccak-256 hash digest of the signed message
                                   * @param signature     Signature byte array associated with hash
                                   *
                                   * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
                                   * change through time. It could return true at block N and false at block N+1 (or the opposite).
                                   */
                                  function isValidERC1271SignatureNow(
                                      address signer,
                                      bytes32 digest,
                                      bytes memory signature
                                  ) internal view returns (bool) {
                                      (bool success, bytes memory result) = signer.staticcall(
                                          abi.encodeWithSelector(
                                              IERC1271.isValidSignature.selector,
                                              digest,
                                              signature
                                          )
                                      );
                                      return (success &&
                                          result.length >= 32 &&
                                          abi.decode(result, (bytes32)) ==
                                          bytes32(IERC1271.isValidSignature.selector));
                                  }
                                  /**
                                   * @dev Checks if the input address is a smart contract.
                                   */
                                  function isContract(address addr) internal view returns (bool) {
                                      uint256 size;
                                      assembly {
                                          size := extcodesize(addr)
                                      }
                                      return size > 0;
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              /**
                               * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
                               *
                               * The library provides methods for generating a hash of a message that conforms to the
                               * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
                               * specifications.
                               */
                              library MessageHashUtils {
                                  /**
                                   * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
                                   * Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/MessageHashUtils.sol
                                   *
                                   * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
                                   * `\\x19\\x01` and hashing the result. It corresponds to the hash signed by the
                                   * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
                                   *
                                   * @param domainSeparator    Domain separator
                                   * @param structHash         Hashed EIP-712 data struct
                                   * @return digest            The keccak256 digest of an EIP-712 typed data
                                   */
                                  function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash)
                                      internal
                                      pure
                                      returns (bytes32 digest)
                                  {
                                      assembly {
                                          let ptr := mload(0x40)
                                          mstore(ptr, "\\x19\\x01")
                                          mstore(add(ptr, 0x02), domainSeparator)
                                          mstore(add(ptr, 0x22), structHash)
                                          digest := keccak256(ptr, 0x42)
                                      }
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              /**
                               * @title EIP712
                               * @notice A library that provides EIP712 helper functions
                               */
                              library EIP712 {
                                  /**
                                   * @notice Make EIP712 domain separator
                                   * @param name      Contract name
                                   * @param version   Contract version
                                   * @param chainId   Blockchain ID
                                   * @return Domain separator
                                   */
                                  function makeDomainSeparator(
                                      string memory name,
                                      string memory version,
                                      uint256 chainId
                                  ) internal view returns (bytes32) {
                                      return
                                          keccak256(
                                              abi.encode(
                                                  // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
                                                  0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
                                                  keccak256(bytes(name)),
                                                  keccak256(bytes(version)),
                                                  chainId,
                                                  address(this)
                                              )
                                          );
                                  }
                                  /**
                                   * @notice Make EIP712 domain separator
                                   * @param name      Contract name
                                   * @param version   Contract version
                                   * @return Domain separator
                                   */
                                  function makeDomainSeparator(string memory name, string memory version)
                                      internal
                                      view
                                      returns (bytes32)
                                  {
                                      uint256 chainId;
                                      assembly {
                                          chainId := chainid()
                                      }
                                      return makeDomainSeparator(name, version, chainId);
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              /**
                               * @title ECRecover
                               * @notice A library that provides a safe ECDSA recovery function
                               */
                              library ECRecover {
                                  /**
                                   * @notice Recover signer's address from a signed message
                                   * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/65e4ffde586ec89af3b7e9140bdc9235d1254853/contracts/cryptography/ECDSA.sol
                                   * Modifications: Accept v, r, and s as separate arguments
                                   * @param digest    Keccak-256 hash digest of the signed message
                                   * @param v         v of the signature
                                   * @param r         r of the signature
                                   * @param s         s of the signature
                                   * @return Signer address
                                   */
                                  function recover(
                                      bytes32 digest,
                                      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.
                                      if (
                                          uint256(s) >
                                          0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
                                      ) {
                                          revert("ECRecover: invalid signature 's' value");
                                      }
                                      if (v != 27 && v != 28) {
                                          revert("ECRecover: invalid signature 'v' value");
                                      }
                                      // If the signature is valid (and not malleable), return the signer address
                                      address signer = ecrecover(digest, v, r, s);
                                      require(signer != address(0), "ECRecover: invalid signature");
                                      return signer;
                                  }
                                  /**
                                   * @notice Recover signer's address from a signed message
                                   * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0053ee040a7ff1dbc39691c9e67a69f564930a88/contracts/utils/cryptography/ECDSA.sol
                                   * @param digest    Keccak-256 hash digest of the signed message
                                   * @param signature Signature byte array associated with hash
                                   * @return Signer address
                                   */
                                  function recover(bytes32 digest, bytes memory signature)
                                      internal
                                      pure
                                      returns (address)
                                  {
                                      require(signature.length == 65, "ECRecover: invalid signature length");
                                      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 recover(digest, v, r, s);
                                  }
                              }
                              /**
                               * SPDX-License-Identifier: Apache-2.0
                               *
                               * Copyright (c) 2023, Circle Internet Financial, LLC.
                               *
                               * 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.12;
                              /**
                               * @dev Interface of the ERC1271 standard signature validation method for
                               * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
                               */
                              interface IERC1271 {
                                  /**
                                   * @dev Should return whether the signature provided is valid for the provided data
                                   * @param hash          Hash of the data to be signed
                                   * @param signature     Signature byte array associated with the provided data hash
                                   * @return magicValue   bytes4 magic value 0x1626ba7e when function passes
                                   */
                                  function isValidSignature(bytes32 hash, bytes memory signature)
                                      external
                                      view
                                      returns (bytes4 magicValue);
                              }
                              

                              File 6 of 6: RelayFacet
                              // SPDX-License-Identifier: MIT
                              pragma solidity ^0.8.17;
                              import { ILiFi } from "../Interfaces/ILiFi.sol";
                              import { LibAsset } from "../Libraries/LibAsset.sol";
                              import { LibSwap } from "../Libraries/LibSwap.sol";
                              import { LibUtil } from "../Libraries/LibUtil.sol";
                              import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol";
                              import { SwapperV2 } from "../Helpers/SwapperV2.sol";
                              import { Validatable } from "../Helpers/Validatable.sol";
                              import { ECDSA } from "solady/utils/ECDSA.sol";
                              /// @title Relay Facet
                              /// @author LI.FI (https://li.fi)
                              /// @notice Provides functionality for bridging through Relay Protocol
                              /// @custom:version 1.0.0
                              contract RelayFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
                                  // Receiver for native transfers
                                  address public immutable relayReceiver;
                                  // Relayer wallet for ERC20 transfers
                                  address public immutable relaySolver;
                                  /// Storage ///
                                  mapping(bytes32 => bool) public consumedIds;
                                  /// Types ///
                                  /// @dev Relay specific parameters
                                  /// @param requestId Relay API request ID
                                  /// @param nonEVMReceiver set only if bridging to non-EVM chain
                                  /// @params receivingAssetId address of receiving asset
                                  /// @params signature attestation signature provided by the Relay solver
                                  struct RelayData {
                                      bytes32 requestId;
                                      bytes32 nonEVMReceiver;
                                      bytes32 receivingAssetId;
                                      bytes signature;
                                  }
                                  /// Events ///
                                  event BridgeToNonEVMChain(
                                      bytes32 indexed transactionId,
                                      uint256 indexed destinationChainId,
                                      bytes32 receiver
                                  );
                                  /// Errors ///
                                  error InvalidQuote();
                                  /// Modifiers ///
                                  /// @param _bridgeData The core information needed for bridging
                                  /// @param _relayData Data specific to Relay
                                  modifier onlyValidQuote(
                                      ILiFi.BridgeData memory _bridgeData,
                                      RelayData calldata _relayData
                                  ) {
                                      // Ensure that the id isn't already consumed
                                      if (consumedIds[_relayData.requestId]) {
                                          revert InvalidQuote();
                                      }
                                      // Ensure nonEVMAddress is not empty
                                      if (
                                          _bridgeData.receiver == LibAsset.NON_EVM_ADDRESS &&
                                          _relayData.nonEVMReceiver == bytes32(0)
                                      ) {
                                          revert InvalidQuote();
                                      }
                                      // Verify that the bridging quote has been signed by the Relay solver
                                      // as attested using the attestation API
                                      // API URL: https://api.relay.link/requests/{requestId}/signature/v2
                                      bytes32 message = ECDSA.toEthSignedMessageHash(
                                          keccak256(
                                              abi.encodePacked(
                                                  _relayData.requestId,
                                                  block.chainid,
                                                  bytes32(uint256(uint160(address(this)))),
                                                  bytes32(uint256(uint160(_bridgeData.sendingAssetId))),
                                                  _getMappedChainId(_bridgeData.destinationChainId),
                                                  _bridgeData.receiver == LibAsset.NON_EVM_ADDRESS
                                                      ? _relayData.nonEVMReceiver
                                                      : bytes32(uint256(uint160(_bridgeData.receiver))),
                                                  _relayData.receivingAssetId
                                              )
                                          )
                                      );
                                      address signer = ECDSA.recover(message, _relayData.signature);
                                      if (signer != relaySolver) {
                                          revert InvalidQuote();
                                      }
                                      _;
                                  }
                                  /// Constructor ///
                                  /// @param _relayReceiver The receiver for native transfers
                                  /// @param _relaySolver The relayer wallet for ERC20 transfers
                                  constructor(address _relayReceiver, address _relaySolver) {
                                      relayReceiver = _relayReceiver;
                                      relaySolver = _relaySolver;
                                  }
                                  /// External Methods ///
                                  /// @notice Bridges tokens via Relay
                                  /// @param _bridgeData The core information needed for bridging
                                  /// @param _relayData Data specific to Relay
                                  function startBridgeTokensViaRelay(
                                      ILiFi.BridgeData calldata _bridgeData,
                                      RelayData calldata _relayData
                                  )
                                      external
                                      payable
                                      nonReentrant
                                      onlyValidQuote(_bridgeData, _relayData)
                                      refundExcessNative(payable(msg.sender))
                                      validateBridgeData(_bridgeData)
                                      doesNotContainSourceSwaps(_bridgeData)
                                      doesNotContainDestinationCalls(_bridgeData)
                                  {
                                      LibAsset.depositAsset(
                                          _bridgeData.sendingAssetId,
                                          _bridgeData.minAmount
                                      );
                                      _startBridge(_bridgeData, _relayData);
                                  }
                                  /// @notice Performs a swap before bridging via Relay
                                  /// @param _bridgeData The core information needed for bridging
                                  /// @param _swapData An array of swap related data for performing swaps before bridging
                                  /// @param _relayData Data specific to Relay
                                  function swapAndStartBridgeTokensViaRelay(
                                      ILiFi.BridgeData memory _bridgeData,
                                      LibSwap.SwapData[] calldata _swapData,
                                      RelayData calldata _relayData
                                  )
                                      external
                                      payable
                                      nonReentrant
                                      onlyValidQuote(_bridgeData, _relayData)
                                      refundExcessNative(payable(msg.sender))
                                      containsSourceSwaps(_bridgeData)
                                      doesNotContainDestinationCalls(_bridgeData)
                                      validateBridgeData(_bridgeData)
                                  {
                                      _bridgeData.minAmount = _depositAndSwap(
                                          _bridgeData.transactionId,
                                          _bridgeData.minAmount,
                                          _swapData,
                                          payable(msg.sender)
                                      );
                                      _startBridge(_bridgeData, _relayData);
                                  }
                                  /// Internal Methods ///
                                  /// @dev Contains the business logic for the bridge via Relay
                                  /// @param _bridgeData The core information needed for bridging
                                  /// @param _relayData Data specific to Relay
                                  function _startBridge(
                                      ILiFi.BridgeData memory _bridgeData,
                                      RelayData calldata _relayData
                                  ) internal {
                                      // check if sendingAsset is native or ERC20
                                      if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) {
                                          // Native
                                          // Send Native to relayReceiver along with requestId as extra data
                                          (bool success, bytes memory reason) = relayReceiver.call{
                                              value: _bridgeData.minAmount
                                          }(abi.encode(_relayData.requestId));
                                          if (!success) {
                                              revert(LibUtil.getRevertMsg(reason));
                                          }
                                      } else {
                                          // ERC20
                                          // We build the calldata from scratch to ensure that we can only
                                          // send to the solver address
                                          bytes memory transferCallData = bytes.concat(
                                              abi.encodeWithSignature(
                                                  "transfer(address,uint256)",
                                                  relaySolver,
                                                  _bridgeData.minAmount
                                              ),
                                              abi.encode(_relayData.requestId)
                                          );
                                          (bool success, bytes memory reason) = address(
                                              _bridgeData.sendingAssetId
                                          ).call(transferCallData);
                                          if (!success) {
                                              revert(LibUtil.getRevertMsg(reason));
                                          }
                                      }
                                      consumedIds[_relayData.requestId] = true;
                                      // Emit special event if bridging to non-EVM chain
                                      if (_bridgeData.receiver == LibAsset.NON_EVM_ADDRESS) {
                                          emit BridgeToNonEVMChain(
                                              _bridgeData.transactionId,
                                              _getMappedChainId(_bridgeData.destinationChainId),
                                              _relayData.nonEVMReceiver
                                          );
                                      }
                                      emit LiFiTransferStarted(_bridgeData);
                                  }
                                  /// @notice get Relay specific chain id for non-EVM chains
                                  ///         IDs found here  https://li.quest/v1/chains?chainTypes=UTXO,SVM
                                  /// @param chainId LIFI specific chain id
                                  function _getMappedChainId(
                                      uint256 chainId
                                  ) internal pure returns (uint256) {
                                      // Bitcoin
                                      if (chainId == 20000000000001) {
                                          return 8253038;
                                      }
                                      // Solana
                                      if (chainId == 1151111081099710) {
                                          return 792703809;
                                      }
                                      return chainId;
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              /// @custom:version 1.0.0
                              pragma solidity ^0.8.17;
                              interface ILiFi {
                                  /// Structs ///
                                  struct BridgeData {
                                      bytes32 transactionId;
                                      string bridge;
                                      string integrator;
                                      address referrer;
                                      address sendingAssetId;
                                      address receiver;
                                      uint256 minAmount;
                                      uint256 destinationChainId;
                                      bool hasSourceSwaps;
                                      bool hasDestinationCall;
                                  }
                                  /// Events ///
                                  event LiFiTransferStarted(ILiFi.BridgeData bridgeData);
                                  event LiFiTransferCompleted(
                                      bytes32 indexed transactionId,
                                      address receivingAssetId,
                                      address receiver,
                                      uint256 amount,
                                      uint256 timestamp
                                  );
                                  event LiFiTransferRecovered(
                                      bytes32 indexed transactionId,
                                      address receivingAssetId,
                                      address receiver,
                                      uint256 amount,
                                      uint256 timestamp
                                  );
                                  event LiFiGenericSwapCompleted(
                                      bytes32 indexed transactionId,
                                      string integrator,
                                      string referrer,
                                      address receiver,
                                      address fromAssetId,
                                      address toAssetId,
                                      uint256 fromAmount,
                                      uint256 toAmount
                                  );
                                  // Deprecated but kept here to include in ABI to parse historic events
                                  event LiFiSwappedGeneric(
                                      bytes32 indexed transactionId,
                                      string integrator,
                                      string referrer,
                                      address fromAssetId,
                                      address toAssetId,
                                      uint256 fromAmount,
                                      uint256 toAmount
                                  );
                              }
                              // SPDX-License-Identifier: UNLICENSED
                              pragma solidity ^0.8.17;
                              import { InsufficientBalance, NullAddrIsNotAnERC20Token, NullAddrIsNotAValidSpender, NoTransferToNullAddress, InvalidAmount, NativeAssetTransferFailed } from "../Errors/GenericErrors.sol";
                              import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                              import { LibSwap } from "./LibSwap.sol";
                              /// @title LibAsset
                              /// @custom:version 1.0.1
                              /// @notice This library contains helpers for dealing with onchain transfers
                              ///         of assets, including accounting for the native asset `assetId`
                              ///         conventions and any noncompliant ERC20 transfers
                              library LibAsset {
                                  uint256 private constant MAX_UINT = type(uint256).max;
                                  address internal constant NULL_ADDRESS = address(0);
                                  address internal constant NON_EVM_ADDRESS =
                                      0x11f111f111f111F111f111f111F111f111f111F1;
                                  /// @dev All native assets use the empty address for their asset id
                                  ///      by convention
                                  address internal constant NATIVE_ASSETID = NULL_ADDRESS; //address(0)
                                  /// @notice Gets the balance of the inheriting contract for the given asset
                                  /// @param assetId The asset identifier to get the balance of
                                  /// @return Balance held by contracts using this library
                                  function getOwnBalance(address assetId) internal view returns (uint256) {
                                      return
                                          isNativeAsset(assetId)
                                              ? address(this).balance
                                              : IERC20(assetId).balanceOf(address(this));
                                  }
                                  /// @notice Transfers ether from the inheriting contract to a given
                                  ///         recipient
                                  /// @param recipient Address to send ether to
                                  /// @param amount Amount to send to given recipient
                                  function transferNativeAsset(
                                      address payable recipient,
                                      uint256 amount
                                  ) private {
                                      if (recipient == NULL_ADDRESS) revert NoTransferToNullAddress();
                                      if (amount > address(this).balance)
                                          revert InsufficientBalance(amount, address(this).balance);
                                      // solhint-disable-next-line avoid-low-level-calls
                                      (bool success, ) = recipient.call{ value: amount }("");
                                      if (!success) revert NativeAssetTransferFailed();
                                  }
                                  /// @notice If the current allowance is insufficient, the allowance for a given spender
                                  /// is set to MAX_UINT.
                                  /// @param assetId Token address to transfer
                                  /// @param spender Address to give spend approval to
                                  /// @param amount Amount to approve for spending
                                  function maxApproveERC20(
                                      IERC20 assetId,
                                      address spender,
                                      uint256 amount
                                  ) internal {
                                      if (isNativeAsset(address(assetId))) {
                                          return;
                                      }
                                      if (spender == NULL_ADDRESS) {
                                          revert NullAddrIsNotAValidSpender();
                                      }
                                      if (assetId.allowance(address(this), spender) < amount) {
                                          SafeERC20.safeApprove(IERC20(assetId), spender, 0);
                                          SafeERC20.safeApprove(IERC20(assetId), spender, MAX_UINT);
                                      }
                                  }
                                  /// @notice Transfers tokens from the inheriting contract to a given
                                  ///         recipient
                                  /// @param assetId Token address to transfer
                                  /// @param recipient Address to send token to
                                  /// @param amount Amount to send to given recipient
                                  function transferERC20(
                                      address assetId,
                                      address recipient,
                                      uint256 amount
                                  ) private {
                                      if (isNativeAsset(assetId)) {
                                          revert NullAddrIsNotAnERC20Token();
                                      }
                                      if (recipient == NULL_ADDRESS) {
                                          revert NoTransferToNullAddress();
                                      }
                                      uint256 assetBalance = IERC20(assetId).balanceOf(address(this));
                                      if (amount > assetBalance) {
                                          revert InsufficientBalance(amount, assetBalance);
                                      }
                                      SafeERC20.safeTransfer(IERC20(assetId), recipient, amount);
                                  }
                                  /// @notice Transfers tokens from a sender to a given recipient
                                  /// @param assetId Token address to transfer
                                  /// @param from Address of sender/owner
                                  /// @param to Address of recipient/spender
                                  /// @param amount Amount to transfer from owner to spender
                                  function transferFromERC20(
                                      address assetId,
                                      address from,
                                      address to,
                                      uint256 amount
                                  ) internal {
                                      if (isNativeAsset(assetId)) {
                                          revert NullAddrIsNotAnERC20Token();
                                      }
                                      if (to == NULL_ADDRESS) {
                                          revert NoTransferToNullAddress();
                                      }
                                      IERC20 asset = IERC20(assetId);
                                      uint256 prevBalance = asset.balanceOf(to);
                                      SafeERC20.safeTransferFrom(asset, from, to, amount);
                                      if (asset.balanceOf(to) - prevBalance != amount) {
                                          revert InvalidAmount();
                                      }
                                  }
                                  function depositAsset(address assetId, uint256 amount) internal {
                                      if (amount == 0) revert InvalidAmount();
                                      if (isNativeAsset(assetId)) {
                                          if (msg.value < amount) revert InvalidAmount();
                                      } else {
                                          uint256 balance = IERC20(assetId).balanceOf(msg.sender);
                                          if (balance < amount) revert InsufficientBalance(amount, balance);
                                          transferFromERC20(assetId, msg.sender, address(this), amount);
                                      }
                                  }
                                  function depositAssets(LibSwap.SwapData[] calldata swaps) internal {
                                      for (uint256 i = 0; i < swaps.length; ) {
                                          LibSwap.SwapData calldata swap = swaps[i];
                                          if (swap.requiresDeposit) {
                                              depositAsset(swap.sendingAssetId, swap.fromAmount);
                                          }
                                          unchecked {
                                              i++;
                                          }
                                      }
                                  }
                                  /// @notice Determines whether the given assetId is the native asset
                                  /// @param assetId The asset identifier to evaluate
                                  /// @return Boolean indicating if the asset is the native asset
                                  function isNativeAsset(address assetId) internal pure returns (bool) {
                                      return assetId == NATIVE_ASSETID;
                                  }
                                  /// @notice Wrapper function to transfer a given asset (native or erc20) to
                                  ///         some recipient. Should handle all non-compliant return value
                                  ///         tokens as well by using the SafeERC20 contract by open zeppelin.
                                  /// @param assetId Asset id for transfer (address(0) for native asset,
                                  ///                token address for erc20s)
                                  /// @param recipient Address to send asset to
                                  /// @param amount Amount to send to given recipient
                                  function transferAsset(
                                      address assetId,
                                      address payable recipient,
                                      uint256 amount
                                  ) internal {
                                      isNativeAsset(assetId)
                                          ? transferNativeAsset(recipient, amount)
                                          : transferERC20(assetId, recipient, amount);
                                  }
                                  /// @dev Checks whether the given address is a contract and contains code
                                  function isContract(address _contractAddr) internal view returns (bool) {
                                      uint256 size;
                                      // solhint-disable-next-line no-inline-assembly
                                      assembly {
                                          size := extcodesize(_contractAddr)
                                      }
                                      return size > 0;
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              /// @custom:version 1.0.0
                              pragma solidity ^0.8.17;
                              import { LibAsset } from "./LibAsset.sol";
                              import { LibUtil } from "./LibUtil.sol";
                              import { InvalidContract, NoSwapFromZeroBalance, InsufficientBalance } from "../Errors/GenericErrors.sol";
                              import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                              library LibSwap {
                                  struct SwapData {
                                      address callTo;
                                      address approveTo;
                                      address sendingAssetId;
                                      address receivingAssetId;
                                      uint256 fromAmount;
                                      bytes callData;
                                      bool requiresDeposit;
                                  }
                                  event AssetSwapped(
                                      bytes32 transactionId,
                                      address dex,
                                      address fromAssetId,
                                      address toAssetId,
                                      uint256 fromAmount,
                                      uint256 toAmount,
                                      uint256 timestamp
                                  );
                                  function swap(bytes32 transactionId, SwapData calldata _swap) internal {
                                      if (!LibAsset.isContract(_swap.callTo)) revert InvalidContract();
                                      uint256 fromAmount = _swap.fromAmount;
                                      if (fromAmount == 0) revert NoSwapFromZeroBalance();
                                      uint256 nativeValue = LibAsset.isNativeAsset(_swap.sendingAssetId)
                                          ? _swap.fromAmount
                                          : 0;
                                      uint256 initialSendingAssetBalance = LibAsset.getOwnBalance(
                                          _swap.sendingAssetId
                                      );
                                      uint256 initialReceivingAssetBalance = LibAsset.getOwnBalance(
                                          _swap.receivingAssetId
                                      );
                                      if (nativeValue == 0) {
                                          LibAsset.maxApproveERC20(
                                              IERC20(_swap.sendingAssetId),
                                              _swap.approveTo,
                                              _swap.fromAmount
                                          );
                                      }
                                      if (initialSendingAssetBalance < _swap.fromAmount) {
                                          revert InsufficientBalance(
                                              _swap.fromAmount,
                                              initialSendingAssetBalance
                                          );
                                      }
                                      // solhint-disable-next-line avoid-low-level-calls
                                      (bool success, bytes memory res) = _swap.callTo.call{
                                          value: nativeValue
                                      }(_swap.callData);
                                      if (!success) {
                                          LibUtil.revertWith(res);
                                      }
                                      uint256 newBalance = LibAsset.getOwnBalance(_swap.receivingAssetId);
                                      emit AssetSwapped(
                                          transactionId,
                                          _swap.callTo,
                                          _swap.sendingAssetId,
                                          _swap.receivingAssetId,
                                          _swap.fromAmount,
                                          newBalance > initialReceivingAssetBalance
                                              ? newBalance - initialReceivingAssetBalance
                                              : newBalance,
                                          block.timestamp
                                      );
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              /// @custom:version 1.0.0
                              pragma solidity ^0.8.17;
                              import "./LibBytes.sol";
                              library LibUtil {
                                  using LibBytes for bytes;
                                  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
                                  }
                                  /// @notice Determines whether the given address is the zero address
                                  /// @param addr The address to verify
                                  /// @return Boolean indicating if the address is the zero address
                                  function isZeroAddress(address addr) internal pure returns (bool) {
                                      return addr == address(0);
                                  }
                                  function revertWith(bytes memory data) internal pure {
                                      assembly {
                                          let dataSize := mload(data) // Load the size of the data
                                          let dataPtr := add(data, 0x20) // Advance data pointer to the next word
                                          revert(dataPtr, dataSize) // Revert with the given data
                                      }
                                  }
                              }
                              // SPDX-License-Identifier: UNLICENSED
                              /// @custom:version 1.0.0
                              pragma solidity ^0.8.17;
                              /// @title Reentrancy Guard
                              /// @author LI.FI (https://li.fi)
                              /// @notice Abstract contract to provide protection against reentrancy
                              abstract contract ReentrancyGuard {
                                  /// Storage ///
                                  bytes32 private constant NAMESPACE = keccak256("com.lifi.reentrancyguard");
                                  /// Types ///
                                  struct ReentrancyStorage {
                                      uint256 status;
                                  }
                                  /// Errors ///
                                  error ReentrancyError();
                                  /// Constants ///
                                  uint256 private constant _NOT_ENTERED = 0;
                                  uint256 private constant _ENTERED = 1;
                                  /// Modifiers ///
                                  modifier nonReentrant() {
                                      ReentrancyStorage storage s = reentrancyStorage();
                                      if (s.status == _ENTERED) revert ReentrancyError();
                                      s.status = _ENTERED;
                                      _;
                                      s.status = _NOT_ENTERED;
                                  }
                                  /// Private Methods ///
                                  /// @dev fetch local storage
                                  function reentrancyStorage()
                                      private
                                      pure
                                      returns (ReentrancyStorage storage data)
                                  {
                                      bytes32 position = NAMESPACE;
                                      // solhint-disable-next-line no-inline-assembly
                                      assembly {
                                          data.slot := position
                                      }
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              /// @custom:version 1.0.0
                              pragma solidity ^0.8.17;
                              import { ILiFi } from "../Interfaces/ILiFi.sol";
                              import { LibSwap } from "../Libraries/LibSwap.sol";
                              import { LibAsset } from "../Libraries/LibAsset.sol";
                              import { LibAllowList } from "../Libraries/LibAllowList.sol";
                              import { ContractCallNotAllowed, NoSwapDataProvided, CumulativeSlippageTooHigh } from "../Errors/GenericErrors.sol";
                              /// @title Swapper
                              /// @author LI.FI (https://li.fi)
                              /// @notice Abstract contract to provide swap functionality
                              contract SwapperV2 is ILiFi {
                                  /// Types ///
                                  /// @dev only used to get around "Stack Too Deep" errors
                                  struct ReserveData {
                                      bytes32 transactionId;
                                      address payable leftoverReceiver;
                                      uint256 nativeReserve;
                                  }
                                  /// Modifiers ///
                                  /// @dev Sends any leftover balances back to the user
                                  /// @notice Sends any leftover balances to the user
                                  /// @param _swaps Swap data array
                                  /// @param _leftoverReceiver Address to send leftover tokens to
                                  /// @param _initialBalances Array of initial token balances
                                  modifier noLeftovers(
                                      LibSwap.SwapData[] calldata _swaps,
                                      address payable _leftoverReceiver,
                                      uint256[] memory _initialBalances
                                  ) {
                                      uint256 numSwaps = _swaps.length;
                                      if (numSwaps != 1) {
                                          address finalAsset = _swaps[numSwaps - 1].receivingAssetId;
                                          uint256 curBalance;
                                          _;
                                          for (uint256 i = 0; i < numSwaps - 1; ) {
                                              address curAsset = _swaps[i].receivingAssetId;
                                              // Handle multi-to-one swaps
                                              if (curAsset != finalAsset) {
                                                  curBalance =
                                                      LibAsset.getOwnBalance(curAsset) -
                                                      _initialBalances[i];
                                                  if (curBalance > 0) {
                                                      LibAsset.transferAsset(
                                                          curAsset,
                                                          _leftoverReceiver,
                                                          curBalance
                                                      );
                                                  }
                                              }
                                              unchecked {
                                                  ++i;
                                              }
                                          }
                                      } else {
                                          _;
                                      }
                                  }
                                  /// @dev Sends any leftover balances back to the user reserving native tokens
                                  /// @notice Sends any leftover balances to the user
                                  /// @param _swaps Swap data array
                                  /// @param _leftoverReceiver Address to send leftover tokens to
                                  /// @param _initialBalances Array of initial token balances
                                  modifier noLeftoversReserve(
                                      LibSwap.SwapData[] calldata _swaps,
                                      address payable _leftoverReceiver,
                                      uint256[] memory _initialBalances,
                                      uint256 _nativeReserve
                                  ) {
                                      uint256 numSwaps = _swaps.length;
                                      if (numSwaps != 1) {
                                          address finalAsset = _swaps[numSwaps - 1].receivingAssetId;
                                          uint256 curBalance;
                                          _;
                                          for (uint256 i = 0; i < numSwaps - 1; ) {
                                              address curAsset = _swaps[i].receivingAssetId;
                                              // Handle multi-to-one swaps
                                              if (curAsset != finalAsset) {
                                                  curBalance =
                                                      LibAsset.getOwnBalance(curAsset) -
                                                      _initialBalances[i];
                                                  uint256 reserve = LibAsset.isNativeAsset(curAsset)
                                                      ? _nativeReserve
                                                      : 0;
                                                  if (curBalance > 0) {
                                                      LibAsset.transferAsset(
                                                          curAsset,
                                                          _leftoverReceiver,
                                                          curBalance - reserve
                                                      );
                                                  }
                                              }
                                              unchecked {
                                                  ++i;
                                              }
                                          }
                                      } else {
                                          _;
                                      }
                                  }
                                  /// @dev Refunds any excess native asset sent to the contract after the main function
                                  /// @notice Refunds any excess native asset sent to the contract after the main function
                                  /// @param _refundReceiver Address to send refunds to
                                  modifier refundExcessNative(address payable _refundReceiver) {
                                      uint256 initialBalance = address(this).balance - msg.value;
                                      _;
                                      uint256 finalBalance = address(this).balance;
                                      if (finalBalance > initialBalance) {
                                          LibAsset.transferAsset(
                                              LibAsset.NATIVE_ASSETID,
                                              _refundReceiver,
                                              finalBalance - initialBalance
                                          );
                                      }
                                  }
                                  /// Internal Methods ///
                                  /// @dev Deposits value, executes swaps, and performs minimum amount check
                                  /// @param _transactionId the transaction id associated with the operation
                                  /// @param _minAmount the minimum amount of the final asset to receive
                                  /// @param _swaps Array of data used to execute swaps
                                  /// @param _leftoverReceiver The address to send leftover funds to
                                  /// @return uint256 result of the swap
                                  function _depositAndSwap(
                                      bytes32 _transactionId,
                                      uint256 _minAmount,
                                      LibSwap.SwapData[] calldata _swaps,
                                      address payable _leftoverReceiver
                                  ) internal returns (uint256) {
                                      uint256 numSwaps = _swaps.length;
                                      if (numSwaps == 0) {
                                          revert NoSwapDataProvided();
                                      }
                                      address finalTokenId = _swaps[numSwaps - 1].receivingAssetId;
                                      uint256 initialBalance = LibAsset.getOwnBalance(finalTokenId);
                                      if (LibAsset.isNativeAsset(finalTokenId)) {
                                          initialBalance -= msg.value;
                                      }
                                      uint256[] memory initialBalances = _fetchBalances(_swaps);
                                      LibAsset.depositAssets(_swaps);
                                      _executeSwaps(
                                          _transactionId,
                                          _swaps,
                                          _leftoverReceiver,
                                          initialBalances
                                      );
                                      uint256 newBalance = LibAsset.getOwnBalance(finalTokenId) -
                                          initialBalance;
                                      if (newBalance < _minAmount) {
                                          revert CumulativeSlippageTooHigh(_minAmount, newBalance);
                                      }
                                      return newBalance;
                                  }
                                  /// @dev Deposits value, executes swaps, and performs minimum amount check and reserves native token for fees
                                  /// @param _transactionId the transaction id associated with the operation
                                  /// @param _minAmount the minimum amount of the final asset to receive
                                  /// @param _swaps Array of data used to execute swaps
                                  /// @param _leftoverReceiver The address to send leftover funds to
                                  /// @param _nativeReserve Amount of native token to prevent from being swept back to the caller
                                  function _depositAndSwap(
                                      bytes32 _transactionId,
                                      uint256 _minAmount,
                                      LibSwap.SwapData[] calldata _swaps,
                                      address payable _leftoverReceiver,
                                      uint256 _nativeReserve
                                  ) internal returns (uint256) {
                                      uint256 numSwaps = _swaps.length;
                                      if (numSwaps == 0) {
                                          revert NoSwapDataProvided();
                                      }
                                      address finalTokenId = _swaps[numSwaps - 1].receivingAssetId;
                                      uint256 initialBalance = LibAsset.getOwnBalance(finalTokenId);
                                      if (LibAsset.isNativeAsset(finalTokenId)) {
                                          initialBalance -= msg.value;
                                      }
                                      uint256[] memory initialBalances = _fetchBalances(_swaps);
                                      LibAsset.depositAssets(_swaps);
                                      ReserveData memory rd = ReserveData(
                                          _transactionId,
                                          _leftoverReceiver,
                                          _nativeReserve
                                      );
                                      _executeSwaps(rd, _swaps, initialBalances);
                                      uint256 newBalance = LibAsset.getOwnBalance(finalTokenId) -
                                          initialBalance;
                                      if (LibAsset.isNativeAsset(finalTokenId)) {
                                          newBalance -= _nativeReserve;
                                      }
                                      if (newBalance < _minAmount) {
                                          revert CumulativeSlippageTooHigh(_minAmount, newBalance);
                                      }
                                      return newBalance;
                                  }
                                  /// Private Methods ///
                                  /// @dev Executes swaps and checks that DEXs used are in the allowList
                                  /// @param _transactionId the transaction id associated with the operation
                                  /// @param _swaps Array of data used to execute swaps
                                  /// @param _leftoverReceiver Address to send leftover tokens to
                                  /// @param _initialBalances Array of initial balances
                                  function _executeSwaps(
                                      bytes32 _transactionId,
                                      LibSwap.SwapData[] calldata _swaps,
                                      address payable _leftoverReceiver,
                                      uint256[] memory _initialBalances
                                  ) internal noLeftovers(_swaps, _leftoverReceiver, _initialBalances) {
                                      uint256 numSwaps = _swaps.length;
                                      for (uint256 i = 0; i < numSwaps; ) {
                                          LibSwap.SwapData calldata currentSwap = _swaps[i];
                                          if (
                                              !((LibAsset.isNativeAsset(currentSwap.sendingAssetId) ||
                                                  LibAllowList.contractIsAllowed(currentSwap.approveTo)) &&
                                                  LibAllowList.contractIsAllowed(currentSwap.callTo) &&
                                                  LibAllowList.selectorIsAllowed(
                                                      bytes4(currentSwap.callData[:4])
                                                  ))
                                          ) revert ContractCallNotAllowed();
                                          LibSwap.swap(_transactionId, currentSwap);
                                          unchecked {
                                              ++i;
                                          }
                                      }
                                  }
                                  /// @dev Executes swaps and checks that DEXs used are in the allowList
                                  /// @param _reserveData Data passed used to reserve native tokens
                                  /// @param _swaps Array of data used to execute swaps
                                  function _executeSwaps(
                                      ReserveData memory _reserveData,
                                      LibSwap.SwapData[] calldata _swaps,
                                      uint256[] memory _initialBalances
                                  )
                                      internal
                                      noLeftoversReserve(
                                          _swaps,
                                          _reserveData.leftoverReceiver,
                                          _initialBalances,
                                          _reserveData.nativeReserve
                                      )
                                  {
                                      uint256 numSwaps = _swaps.length;
                                      for (uint256 i = 0; i < numSwaps; ) {
                                          LibSwap.SwapData calldata currentSwap = _swaps[i];
                                          if (
                                              !((LibAsset.isNativeAsset(currentSwap.sendingAssetId) ||
                                                  LibAllowList.contractIsAllowed(currentSwap.approveTo)) &&
                                                  LibAllowList.contractIsAllowed(currentSwap.callTo) &&
                                                  LibAllowList.selectorIsAllowed(
                                                      bytes4(currentSwap.callData[:4])
                                                  ))
                                          ) revert ContractCallNotAllowed();
                                          LibSwap.swap(_reserveData.transactionId, currentSwap);
                                          unchecked {
                                              ++i;
                                          }
                                      }
                                  }
                                  /// @dev Fetches balances of tokens to be swapped before swapping.
                                  /// @param _swaps Array of data used to execute swaps
                                  /// @return uint256[] Array of token balances.
                                  function _fetchBalances(
                                      LibSwap.SwapData[] calldata _swaps
                                  ) private view returns (uint256[] memory) {
                                      uint256 numSwaps = _swaps.length;
                                      uint256[] memory balances = new uint256[](numSwaps);
                                      address asset;
                                      for (uint256 i = 0; i < numSwaps; ) {
                                          asset = _swaps[i].receivingAssetId;
                                          balances[i] = LibAsset.getOwnBalance(asset);
                                          if (LibAsset.isNativeAsset(asset)) {
                                              balances[i] -= msg.value;
                                          }
                                          unchecked {
                                              ++i;
                                          }
                                      }
                                      return balances;
                                  }
                              }
                              // SPDX-License-Identifier: UNLICENSED
                              /// @custom:version 1.0.0
                              pragma solidity ^0.8.17;
                              import { LibAsset } from "../Libraries/LibAsset.sol";
                              import { LibUtil } from "../Libraries/LibUtil.sol";
                              import { InvalidReceiver, InformationMismatch, InvalidSendingToken, InvalidAmount, NativeAssetNotSupported, InvalidDestinationChain, CannotBridgeToSameNetwork } from "../Errors/GenericErrors.sol";
                              import { ILiFi } from "../Interfaces/ILiFi.sol";
                              import { LibSwap } from "../Libraries/LibSwap.sol";
                              contract Validatable {
                                  modifier validateBridgeData(ILiFi.BridgeData memory _bridgeData) {
                                      if (LibUtil.isZeroAddress(_bridgeData.receiver)) {
                                          revert InvalidReceiver();
                                      }
                                      if (_bridgeData.minAmount == 0) {
                                          revert InvalidAmount();
                                      }
                                      if (_bridgeData.destinationChainId == block.chainid) {
                                          revert CannotBridgeToSameNetwork();
                                      }
                                      _;
                                  }
                                  modifier noNativeAsset(ILiFi.BridgeData memory _bridgeData) {
                                      if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) {
                                          revert NativeAssetNotSupported();
                                      }
                                      _;
                                  }
                                  modifier onlyAllowSourceToken(
                                      ILiFi.BridgeData memory _bridgeData,
                                      address _token
                                  ) {
                                      if (_bridgeData.sendingAssetId != _token) {
                                          revert InvalidSendingToken();
                                      }
                                      _;
                                  }
                                  modifier onlyAllowDestinationChain(
                                      ILiFi.BridgeData memory _bridgeData,
                                      uint256 _chainId
                                  ) {
                                      if (_bridgeData.destinationChainId != _chainId) {
                                          revert InvalidDestinationChain();
                                      }
                                      _;
                                  }
                                  modifier containsSourceSwaps(ILiFi.BridgeData memory _bridgeData) {
                                      if (!_bridgeData.hasSourceSwaps) {
                                          revert InformationMismatch();
                                      }
                                      _;
                                  }
                                  modifier doesNotContainSourceSwaps(ILiFi.BridgeData memory _bridgeData) {
                                      if (_bridgeData.hasSourceSwaps) {
                                          revert InformationMismatch();
                                      }
                                      _;
                                  }
                                  modifier doesNotContainDestinationCalls(
                                      ILiFi.BridgeData memory _bridgeData
                                  ) {
                                      if (_bridgeData.hasDestinationCall) {
                                          revert InformationMismatch();
                                      }
                                      _;
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              pragma solidity ^0.8.4;
                              /// @notice Gas optimized ECDSA wrapper.
                              /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
                              /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
                              /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
                              ///
                              /// @dev Note:
                              /// - The recovery functions use the ecrecover precompile (0x1).
                              /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
                              ///   This is for more safety by default.
                              ///   Use the `tryRecover` variants if you need to get the zero address back
                              ///   upon recovery failure instead.
                              /// - As of Solady version 0.0.134, all `bytes signature` variants accept both
                              ///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
                              ///   See: https://eips.ethereum.org/EIPS/eip-2098
                              ///   This is for calldata efficiency on smart accounts prevalent on L2s.
                              ///
                              /// WARNING! Do NOT use signatures as unique identifiers:
                              /// - Use a nonce in the digest to prevent replay attacks on the same contract.
                              /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
                              ///   EIP-712 also enables readable signing of typed data for better user safety.
                              /// This implementation does NOT check if a signature is non-malleable.
                              library ECDSA {
                                  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                                  /*                        CUSTOM ERRORS                       */
                                  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                                  /// @dev The signature is invalid.
                                  error InvalidSignature();
                                  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                                  /*                    RECOVERY OPERATIONS                     */
                                  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                                  /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
                                  function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          result := 1
                                          let m := mload(0x40) // Cache the free memory pointer.
                                          for {} 1 {} {
                                              mstore(0x00, hash)
                                              mstore(0x40, mload(add(signature, 0x20))) // `r`.
                                              if eq(mload(signature), 64) {
                                                  let vs := mload(add(signature, 0x40))
                                                  mstore(0x20, add(shr(255, vs), 27)) // `v`.
                                                  mstore(0x60, shr(1, shl(1, vs))) // `s`.
                                                  break
                                              }
                                              if eq(mload(signature), 65) {
                                                  mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                                                  mstore(0x60, mload(add(signature, 0x40))) // `s`.
                                                  break
                                              }
                                              result := 0
                                              break
                                          }
                                          result :=
                                              mload(
                                                  staticcall(
                                                      gas(), // Amount of gas left for the transaction.
                                                      result, // Address of `ecrecover`.
                                                      0x00, // Start of input.
                                                      0x80, // Size of input.
                                                      0x01, // Start of output.
                                                      0x20 // Size of output.
                                                  )
                                              )
                                          // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                                          if iszero(returndatasize()) {
                                              mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                                              revert(0x1c, 0x04)
                                          }
                                          mstore(0x60, 0) // Restore the zero slot.
                                          mstore(0x40, m) // Restore the free memory pointer.
                                      }
                                  }
                                  /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
                                  function recoverCalldata(bytes32 hash, bytes calldata signature)
                                      internal
                                      view
                                      returns (address result)
                                  {
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          result := 1
                                          let m := mload(0x40) // Cache the free memory pointer.
                                          mstore(0x00, hash)
                                          for {} 1 {} {
                                              if eq(signature.length, 64) {
                                                  let vs := calldataload(add(signature.offset, 0x20))
                                                  mstore(0x20, add(shr(255, vs), 27)) // `v`.
                                                  mstore(0x40, calldataload(signature.offset)) // `r`.
                                                  mstore(0x60, shr(1, shl(1, vs))) // `s`.
                                                  break
                                              }
                                              if eq(signature.length, 65) {
                                                  mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                                                  calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                                                  break
                                              }
                                              result := 0
                                              break
                                          }
                                          result :=
                                              mload(
                                                  staticcall(
                                                      gas(), // Amount of gas left for the transaction.
                                                      result, // Address of `ecrecover`.
                                                      0x00, // Start of input.
                                                      0x80, // Size of input.
                                                      0x01, // Start of output.
                                                      0x20 // Size of output.
                                                  )
                                              )
                                          // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                                          if iszero(returndatasize()) {
                                              mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                                              revert(0x1c, 0x04)
                                          }
                                          mstore(0x60, 0) // Restore the zero slot.
                                          mstore(0x40, m) // Restore the free memory pointer.
                                      }
                                  }
                                  /// @dev Recovers the signer's address from a message digest `hash`,
                                  /// and the EIP-2098 short form signature defined by `r` and `vs`.
                                  function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          let m := mload(0x40) // Cache the free memory pointer.
                                          mstore(0x00, hash)
                                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                                          mstore(0x40, r)
                                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                                          result :=
                                              mload(
                                                  staticcall(
                                                      gas(), // Amount of gas left for the transaction.
                                                      1, // Address of `ecrecover`.
                                                      0x00, // Start of input.
                                                      0x80, // Size of input.
                                                      0x01, // Start of output.
                                                      0x20 // Size of output.
                                                  )
                                              )
                                          // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                                          if iszero(returndatasize()) {
                                              mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                                              revert(0x1c, 0x04)
                                          }
                                          mstore(0x60, 0) // Restore the zero slot.
                                          mstore(0x40, m) // Restore the free memory pointer.
                                      }
                                  }
                                  /// @dev Recovers the signer's address from a message digest `hash`,
                                  /// and the signature defined by `v`, `r`, `s`.
                                  function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
                                      internal
                                      view
                                      returns (address result)
                                  {
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          let m := mload(0x40) // Cache the free memory pointer.
                                          mstore(0x00, hash)
                                          mstore(0x20, and(v, 0xff))
                                          mstore(0x40, r)
                                          mstore(0x60, s)
                                          result :=
                                              mload(
                                                  staticcall(
                                                      gas(), // Amount of gas left for the transaction.
                                                      1, // Address of `ecrecover`.
                                                      0x00, // Start of input.
                                                      0x80, // Size of input.
                                                      0x01, // Start of output.
                                                      0x20 // Size of output.
                                                  )
                                              )
                                          // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                                          if iszero(returndatasize()) {
                                              mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                                              revert(0x1c, 0x04)
                                          }
                                          mstore(0x60, 0) // Restore the zero slot.
                                          mstore(0x40, m) // Restore the free memory pointer.
                                      }
                                  }
                                  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                                  /*                   TRY-RECOVER OPERATIONS                   */
                                  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                                  // WARNING!
                                  // These functions will NOT revert upon recovery failure.
                                  // Instead, they will return the zero address upon recovery failure.
                                  // It is critical that the returned address is NEVER compared against
                                  // a zero address (e.g. an uninitialized address variable).
                                  /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
                                  function tryRecover(bytes32 hash, bytes memory signature)
                                      internal
                                      view
                                      returns (address result)
                                  {
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          result := 1
                                          let m := mload(0x40) // Cache the free memory pointer.
                                          for {} 1 {} {
                                              mstore(0x00, hash)
                                              mstore(0x40, mload(add(signature, 0x20))) // `r`.
                                              if eq(mload(signature), 64) {
                                                  let vs := mload(add(signature, 0x40))
                                                  mstore(0x20, add(shr(255, vs), 27)) // `v`.
                                                  mstore(0x60, shr(1, shl(1, vs))) // `s`.
                                                  break
                                              }
                                              if eq(mload(signature), 65) {
                                                  mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                                                  mstore(0x60, mload(add(signature, 0x40))) // `s`.
                                                  break
                                              }
                                              result := 0
                                              break
                                          }
                                          pop(
                                              staticcall(
                                                  gas(), // Amount of gas left for the transaction.
                                                  result, // Address of `ecrecover`.
                                                  0x00, // Start of input.
                                                  0x80, // Size of input.
                                                  0x40, // Start of output.
                                                  0x20 // Size of output.
                                              )
                                          )
                                          mstore(0x60, 0) // Restore the zero slot.
                                          // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                                          result := mload(xor(0x60, returndatasize()))
                                          mstore(0x40, m) // Restore the free memory pointer.
                                      }
                                  }
                                  /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
                                  function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
                                      internal
                                      view
                                      returns (address result)
                                  {
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          result := 1
                                          let m := mload(0x40) // Cache the free memory pointer.
                                          mstore(0x00, hash)
                                          for {} 1 {} {
                                              if eq(signature.length, 64) {
                                                  let vs := calldataload(add(signature.offset, 0x20))
                                                  mstore(0x20, add(shr(255, vs), 27)) // `v`.
                                                  mstore(0x40, calldataload(signature.offset)) // `r`.
                                                  mstore(0x60, shr(1, shl(1, vs))) // `s`.
                                                  break
                                              }
                                              if eq(signature.length, 65) {
                                                  mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                                                  calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                                                  break
                                              }
                                              result := 0
                                              break
                                          }
                                          pop(
                                              staticcall(
                                                  gas(), // Amount of gas left for the transaction.
                                                  result, // Address of `ecrecover`.
                                                  0x00, // Start of input.
                                                  0x80, // Size of input.
                                                  0x40, // Start of output.
                                                  0x20 // Size of output.
                                              )
                                          )
                                          mstore(0x60, 0) // Restore the zero slot.
                                          // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                                          result := mload(xor(0x60, returndatasize()))
                                          mstore(0x40, m) // Restore the free memory pointer.
                                      }
                                  }
                                  /// @dev Recovers the signer's address from a message digest `hash`,
                                  /// and the EIP-2098 short form signature defined by `r` and `vs`.
                                  function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
                                      internal
                                      view
                                      returns (address result)
                                  {
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          let m := mload(0x40) // Cache the free memory pointer.
                                          mstore(0x00, hash)
                                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                                          mstore(0x40, r)
                                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                                          pop(
                                              staticcall(
                                                  gas(), // Amount of gas left for the transaction.
                                                  1, // Address of `ecrecover`.
                                                  0x00, // Start of input.
                                                  0x80, // Size of input.
                                                  0x40, // Start of output.
                                                  0x20 // Size of output.
                                              )
                                          )
                                          mstore(0x60, 0) // Restore the zero slot.
                                          // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                                          result := mload(xor(0x60, returndatasize()))
                                          mstore(0x40, m) // Restore the free memory pointer.
                                      }
                                  }
                                  /// @dev Recovers the signer's address from a message digest `hash`,
                                  /// and the signature defined by `v`, `r`, `s`.
                                  function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
                                      internal
                                      view
                                      returns (address result)
                                  {
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          let m := mload(0x40) // Cache the free memory pointer.
                                          mstore(0x00, hash)
                                          mstore(0x20, and(v, 0xff))
                                          mstore(0x40, r)
                                          mstore(0x60, s)
                                          pop(
                                              staticcall(
                                                  gas(), // Amount of gas left for the transaction.
                                                  1, // Address of `ecrecover`.
                                                  0x00, // Start of input.
                                                  0x80, // Size of input.
                                                  0x40, // Start of output.
                                                  0x20 // Size of output.
                                              )
                                          )
                                          mstore(0x60, 0) // Restore the zero slot.
                                          // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                                          result := mload(xor(0x60, returndatasize()))
                                          mstore(0x40, m) // Restore the free memory pointer.
                                      }
                                  }
                                  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                                  /*                     HASHING OPERATIONS                     */
                                  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                                  /// @dev Returns an Ethereum Signed Message, created from a `hash`.
                                  /// This produces a hash corresponding to the one signed with the
                                  /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
                                  /// JSON-RPC method as part of EIP-191.
                                  function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          mstore(0x20, hash) // Store into scratch space for keccak256.
                                          mstore(0x00, "\\x00\\x00\\x00\\x00\\x19Ethereum Signed Message:\
                              32") // 28 bytes.
                                          result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
                                      }
                                  }
                                  /// @dev Returns an Ethereum Signed Message, created from `s`.
                                  /// This produces a hash corresponding to the one signed with the
                                  /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
                                  /// JSON-RPC method as part of EIP-191.
                                  /// Note: Supports lengths of `s` up to 999999 bytes.
                                  function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          let sLength := mload(s)
                                          let o := 0x20
                                          mstore(o, "\\x19Ethereum Signed Message:\
                              ") // 26 bytes, zero-right-padded.
                                          mstore(0x00, 0x00)
                                          // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
                                          for { let temp := sLength } 1 {} {
                                              o := sub(o, 1)
                                              mstore8(o, add(48, mod(temp, 10)))
                                              temp := div(temp, 10)
                                              if iszero(temp) { break }
                                          }
                                          let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
                                          // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
                                          returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
                                          mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
                                          result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
                                          mstore(s, sLength) // Restore the length.
                                      }
                                  }
                                  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                                  /*                   EMPTY CALLDATA HELPERS                   */
                                  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                                  /// @dev Returns an empty calldata bytes.
                                  function emptySignature() internal pure returns (bytes calldata signature) {
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          signature.length := 0
                                      }
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              /// @custom:version 1.0.0
                              pragma solidity ^0.8.17;
                              error AlreadyInitialized();
                              error CannotAuthoriseSelf();
                              error CannotBridgeToSameNetwork();
                              error ContractCallNotAllowed();
                              error CumulativeSlippageTooHigh(uint256 minAmount, uint256 receivedAmount);
                              error DiamondIsPaused();
                              error ExternalCallFailed();
                              error FunctionDoesNotExist();
                              error InformationMismatch();
                              error InsufficientBalance(uint256 required, uint256 balance);
                              error InvalidAmount();
                              error InvalidCallData();
                              error InvalidConfig();
                              error InvalidContract();
                              error InvalidDestinationChain();
                              error InvalidFallbackAddress();
                              error InvalidReceiver();
                              error InvalidSendingToken();
                              error NativeAssetNotSupported();
                              error NativeAssetTransferFailed();
                              error NoSwapDataProvided();
                              error NoSwapFromZeroBalance();
                              error NotAContract();
                              error NotInitialized();
                              error NoTransferToNullAddress();
                              error NullAddrIsNotAnERC20Token();
                              error NullAddrIsNotAValidSpender();
                              error OnlyContractOwner();
                              error RecoveryAddressCannotBeZero();
                              error ReentrancyError();
                              error TokenNotSupported();
                              error UnAuthorized();
                              error UnsupportedChainId(uint256 chainId);
                              error WithdrawFailed();
                              error ZeroAmount();
                              // SPDX-License-Identifier: MIT
                              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)
                              pragma solidity ^0.8.0;
                              import "../IERC20.sol";
                              import "../extensions/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;
                                  /**
                                   * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
                                   * non-reverting calls are assumed to be successful.
                                   */
                                  function safeTransfer(IERC20 token, address to, uint256 value) internal {
                                      _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                                  }
                                  /**
                                   * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
                                   * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
                                   */
                                  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));
                                  }
                                  /**
                                   * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                                   * non-reverting calls are assumed to be successful.
                                   */
                                  function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                                      uint256 oldAllowance = token.allowance(address(this), spender);
                                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
                                  }
                                  /**
                                   * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                                   * non-reverting calls are assumed to be successful.
                                   */
                                  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");
                                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
                                      }
                                  }
                                  /**
                                   * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
                                   * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
                                   * 0 before setting it to a non-zero value.
                                   */
                                  function forceApprove(IERC20 token, address spender, uint256 value) internal {
                                      bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
                                      if (!_callOptionalReturnBool(token, approvalCall)) {
                                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                                          _callOptionalReturn(token, approvalCall);
                                      }
                                  }
                                  /**
                                   * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
                                   * Revert on invalid signature.
                                   */
                                  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");
                                      require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation 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).
                                   *
                                   * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
                                   */
                                  function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                                      // 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 cannot use {Address-functionCall} here since this should return false
                                      // and not revert is the subcall reverts.
                                      (bool success, bytes memory returndata) = address(token).call(data);
                                      return
                                          success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              // OpenZeppelin Contracts (last updated v4.9.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
                              /// @custom:version 1.0.0
                              pragma solidity ^0.8.17;
                              library LibBytes {
                                  // solhint-disable no-inline-assembly
                                  // LibBytes specific errors
                                  error SliceOverflow();
                                  error SliceOutOfBounds();
                                  error AddressOutOfBounds();
                                  bytes16 private constant _SYMBOLS = "0123456789abcdef";
                                  // -------------------------
                                  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;
                                  }
                                  function toAddress(
                                      bytes memory _bytes,
                                      uint256 _start
                                  ) internal pure returns (address) {
                                      if (_bytes.length < _start + 20) {
                                          revert AddressOutOfBounds();
                                      }
                                      address tempAddress;
                                      assembly {
                                          tempAddress := div(
                                              mload(add(add(_bytes, 0x20), _start)),
                                              0x1000000000000000000000000
                                          )
                                      }
                                      return tempAddress;
                                  }
                                  /// Copied from OpenZeppelin's `Strings.sol` utility library.
                                  /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/8335676b0e99944eef6a742e16dcd9ff6e68e609/contracts/utils/Strings.sol
                                  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);
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              /// @custom:version 1.0.0
                              pragma solidity ^0.8.17;
                              import { InvalidContract } from "../Errors/GenericErrors.sol";
                              /// @title Lib Allow List
                              /// @author LI.FI (https://li.fi)
                              /// @notice Library for managing and accessing the conract address allow list
                              library LibAllowList {
                                  /// Storage ///
                                  bytes32 internal constant NAMESPACE =
                                      keccak256("com.lifi.library.allow.list");
                                  struct AllowListStorage {
                                      mapping(address => bool) allowlist;
                                      mapping(bytes4 => bool) selectorAllowList;
                                      address[] contracts;
                                  }
                                  /// @dev Adds a contract address to the allow list
                                  /// @param _contract the contract address to add
                                  function addAllowedContract(address _contract) internal {
                                      _checkAddress(_contract);
                                      AllowListStorage storage als = _getStorage();
                                      if (als.allowlist[_contract]) return;
                                      als.allowlist[_contract] = true;
                                      als.contracts.push(_contract);
                                  }
                                  /// @dev Checks whether a contract address has been added to the allow list
                                  /// @param _contract the contract address to check
                                  function contractIsAllowed(
                                      address _contract
                                  ) internal view returns (bool) {
                                      return _getStorage().allowlist[_contract];
                                  }
                                  /// @dev Remove a contract address from the allow list
                                  /// @param _contract the contract address to remove
                                  function removeAllowedContract(address _contract) internal {
                                      AllowListStorage storage als = _getStorage();
                                      if (!als.allowlist[_contract]) {
                                          return;
                                      }
                                      als.allowlist[_contract] = false;
                                      uint256 length = als.contracts.length;
                                      // Find the contract in the list
                                      for (uint256 i = 0; i < length; i++) {
                                          if (als.contracts[i] == _contract) {
                                              // Move the last element into the place to delete
                                              als.contracts[i] = als.contracts[length - 1];
                                              // Remove the last element
                                              als.contracts.pop();
                                              break;
                                          }
                                      }
                                  }
                                  /// @dev Fetch contract addresses from the allow list
                                  function getAllowedContracts() internal view returns (address[] memory) {
                                      return _getStorage().contracts;
                                  }
                                  /// @dev Add a selector to the allow list
                                  /// @param _selector the selector to add
                                  function addAllowedSelector(bytes4 _selector) internal {
                                      _getStorage().selectorAllowList[_selector] = true;
                                  }
                                  /// @dev Removes a selector from the allow list
                                  /// @param _selector the selector to remove
                                  function removeAllowedSelector(bytes4 _selector) internal {
                                      _getStorage().selectorAllowList[_selector] = false;
                                  }
                                  /// @dev Returns if selector has been added to the allow list
                                  /// @param _selector the selector to check
                                  function selectorIsAllowed(bytes4 _selector) internal view returns (bool) {
                                      return _getStorage().selectorAllowList[_selector];
                                  }
                                  /// @dev Fetch local storage struct
                                  function _getStorage()
                                      internal
                                      pure
                                      returns (AllowListStorage storage als)
                                  {
                                      bytes32 position = NAMESPACE;
                                      // solhint-disable-next-line no-inline-assembly
                                      assembly {
                                          als.slot := position
                                      }
                                  }
                                  /// @dev Contains business logic for validating a contract address.
                                  /// @param _contract address of the dex to check
                                  function _checkAddress(address _contract) private view {
                                      if (_contract == address(0)) revert InvalidContract();
                                      if (_contract.code.length == 0) revert InvalidContract();
                                  }
                              }
                              // SPDX-License-Identifier: MIT
                              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/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.9.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
                                   *
                                   * Furthermore, `isContract` will also return true if the target contract within
                                   * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                                   * which only has an effect at the end of a transaction.
                                   * ====
                                   *
                                   * [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://consensys.net/diligence/blog/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.8.0/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);
                                      }
                                  }
                              }